Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional proxy to tus client & uploader #84

Merged
merged 2 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ public void prepareConnection(@NotNull HttpURLConnection connection) {
}
```

### Can I use a proxy that will be used for uploading files?

Yes, just add a proxy to the TusClient as shown below (1 line added to the above [usage](#usage)):

```java
TusClient client = new TusClient();
Proxy myProxy = new Proxy(...);
client.setProxy(myProxy);
Acconut marked this conversation as resolved.
Show resolved Hide resolved
```

## License

MIT
44 changes: 40 additions & 4 deletions src/main/java/io/tus/java/client/TusClient.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.tus.java.client;

import java.net.Proxy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -19,6 +20,7 @@ public class TusClient {
public static final String TUS_VERSION = "1.0.0";

private URL uploadCreationURL;
private Proxy proxy;
private boolean resumingEnabled;
private boolean removeFingerprintOnSuccessEnabled;
private TusURLStore urlStore;
Expand Down Expand Up @@ -52,6 +54,24 @@ public URL getUploadCreationURL() {
return uploadCreationURL;
}

/**
* Set the proxy that will be used for all requests.
*
* @param proxy Proxy to use
*/
public void setProxy(Proxy proxy) {
this.proxy = proxy;
}

/**
* Get the current proxy used for all requests.
*
* @return Current proxy
*/
public Proxy getProxy() {
return proxy;
}

/**
* Enable resuming already started uploads. This step is required if you want to use
* {@link #resumeUpload(TusUpload)}.
Expand Down Expand Up @@ -174,7 +194,7 @@ public int getConnectTimeout() {
* @throws IOException Thrown if an exception occurs while issuing the HTTP request.
*/
public TusUploader createUpload(@NotNull TusUpload upload) throws ProtocolException, IOException {
HttpURLConnection connection = (HttpURLConnection) uploadCreationURL.openConnection();
HttpURLConnection connection = openConnection(uploadCreationURL);
connection.setRequestMethod("POST");
prepareConnection(connection);

Expand Down Expand Up @@ -206,7 +226,23 @@ public TusUploader createUpload(@NotNull TusUpload upload) throws ProtocolExcept
urlStore.set(upload.getFingerprint(), uploadURL);
}

return new TusUploader(this, upload, uploadURL, upload.getTusInputStream(), 0);
return createUploader(upload, uploadURL, 0L);
}

@NotNull
private HttpURLConnection openConnection(@NotNull URL uploadURL) throws IOException {
if (proxy != null) {
return (HttpURLConnection) uploadURL.openConnection(proxy);
}
return (HttpURLConnection) uploadURL.openConnection();
}

@NotNull
private TusUploader createUploader(@NotNull TusUpload upload, @NotNull URL uploadURL, long offset)
throws IOException {
TusUploader uploader = new TusUploader(this, upload, uploadURL, upload.getTusInputStream(), offset);
uploader.setProxy(proxy);
return uploader;
}

/**
Expand Down Expand Up @@ -259,7 +295,7 @@ public TusUploader resumeUpload(@NotNull TusUpload upload) throws
*/
public TusUploader beginOrResumeUploadFromURL(@NotNull TusUpload upload, @NotNull URL uploadURL) throws
ProtocolException, IOException {
HttpURLConnection connection = (HttpURLConnection) uploadURL.openConnection();
HttpURLConnection connection = openConnection(uploadURL);
connection.setRequestMethod("HEAD");
prepareConnection(connection);

Expand All @@ -277,7 +313,7 @@ public TusUploader beginOrResumeUploadFromURL(@NotNull TusUpload upload, @NotNul
}
long offset = Long.parseLong(offsetStr);

return new TusUploader(this, upload, uploadURL, upload.getTusInputStream(), offset);
return createUploader(upload, uploadURL, offset);
}

/**
Expand Down
28 changes: 26 additions & 2 deletions src/main/java/io/tus/java/client/TusUploader.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;

Expand All @@ -21,6 +22,7 @@
*/
public class TusUploader {
private URL uploadURL;
private Proxy proxy;
private TusInputStream input;
private long offset;
private TusClient client;
Expand All @@ -44,7 +46,7 @@ public class TusUploader {
* @throws IOException Thrown if an exception occurs while issuing the HTTP request.
*/
public TusUploader(TusClient client, TusUpload upload, URL uploadURL, TusInputStream input, long offset)
throws IOException {
throws IOException {
this.uploadURL = uploadURL;
this.input = input;
this.offset = offset;
Expand All @@ -65,7 +67,11 @@ private void openConnection() throws IOException, ProtocolException {
bytesRemainingForRequest = requestPayloadSize;
input.mark(requestPayloadSize);

connection = (HttpURLConnection) uploadURL.openConnection();
if (proxy != null) {
connection = (HttpURLConnection) uploadURL.openConnection(proxy);
} else {
connection = (HttpURLConnection) uploadURL.openConnection();
}
client.prepareConnection(connection);
connection.setRequestProperty("Upload-Offset", Long.toString(offset));
connection.setRequestProperty("Content-Type", "application/offset+octet-stream");
Expand Down Expand Up @@ -268,6 +274,24 @@ public URL getUploadURL() {
return uploadURL;
}

/**
* Set the proxy that will be used when uploading.
*
* @param proxy Proxy to use
*/
public void setProxy(Proxy proxy) {
Acconut marked this conversation as resolved.
Show resolved Hide resolved
this.proxy = proxy;
}

/**
* This methods returns the proxy used when uploading.
*
* @return The {@link Proxy} used for the upload or null when not set.
*/
public Proxy getProxy() {
return proxy;
}

/**
* Finish the request by closing the HTTP connection and the InputStream.
* You can call this method even before the entire file has been uploaded. Use this behavior to
Expand Down
70 changes: 70 additions & 0 deletions src/test/java/io/tus/java/client/TestTusClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -80,6 +83,7 @@ public void testCreateUpload() throws IOException, ProtocolException {
mockServer.when(new HttpRequest()
.withMethod("POST")
.withPath("/files")
.withHeader("Connection", "keep-alive")
pdenooijer marked this conversation as resolved.
Show resolved Hide resolved
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Upload-Metadata", "foo aGVsbG8=,bar d29ybGQ=")
.withHeader("Upload-Length", "10"))
Expand All @@ -102,6 +106,40 @@ public void testCreateUpload() throws IOException, ProtocolException {

assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));
}
/**
* Verifies if uploads can be created with the tus client through a proxy.
* @throws IOException if upload data cannot be read.
* @throws ProtocolException if the upload cannot be constructed.
*/
@Test
public void testCreateUploadWithProxy() throws IOException, ProtocolException {
mockServer.when(new HttpRequest()
.withMethod("POST")
.withPath("/files")
.withHeader("Proxy-Connection", "keep-alive")
pdenooijer marked this conversation as resolved.
Show resolved Hide resolved
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Upload-Metadata", "foo aGVsbG8=,bar d29ybGQ=")
.withHeader("Upload-Length", "11"))
.respond(new HttpResponse()
.withStatusCode(201)
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Location", mockServerURL + "/foo"));

Map<String, String> metadata = new LinkedHashMap<String, String>();
metadata.put("foo", "hello");
metadata.put("bar", "world");

TusClient client = new TusClient();
client.setUploadCreationURL(mockServerURL);
client.setProxy(new Proxy(Type.HTTP, new InetSocketAddress("localhost", mockServer.getPort())));
TusUpload upload = new TusUpload();
upload.setSize(11);
upload.setInputStream(new ByteArrayInputStream(new byte[11]));
upload.setMetadata(metadata);
TusUploader uploader = client.createUpload(upload);

assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));
}

/**
* Tests if a missing location header causes an exception as expected.
Expand Down Expand Up @@ -239,6 +277,7 @@ public void testResumeOrCreateUpload() throws IOException, ProtocolException {
mockServer.when(new HttpRequest()
.withMethod("POST")
.withPath("/files")
.withHeader("Connection", "keep-alive")
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Upload-Length", "10"))
.respond(new HttpResponse()
Expand All @@ -256,6 +295,37 @@ public void testResumeOrCreateUpload() throws IOException, ProtocolException {
assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));
}

/**
* Tests if an upload gets started when {@link TusClient#resumeOrCreateUpload(TusUpload)} gets called with
* a proxy set.
* @throws IOException
* @throws ProtocolException
*/
@Test
public void testResumeOrCreateUploadWithProxy() throws IOException, ProtocolException {
mockServer.when(new HttpRequest()
.withMethod("POST")
.withPath("/files")
.withHeader("Proxy-Connection", "keep-alive")
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Upload-Length", "11"))
.respond(new HttpResponse()
.withStatusCode(201)
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Location", mockServerURL + "/foo"));

TusClient client = new TusClient();
client.setUploadCreationURL(mockServerURL);
Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress("localhost", mockServer.getPort()));
client.setProxy(proxy);
TusUpload upload = new TusUpload();
upload.setSize(11);
upload.setInputStream(new ByteArrayInputStream(new byte[11]));
TusUploader uploader = client.resumeOrCreateUpload(upload);

assertEquals(proxy, client.getProxy());
assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));
}

/**
* Checks if a new upload attempt is started in case of a serverside 404-error, without having an Exception thrown.
Expand Down
41 changes: 41 additions & 0 deletions src/test/java/io/tus/java/client/TestTusUploader.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
Expand Down Expand Up @@ -44,6 +47,7 @@ public void testTusUploader() throws IOException, ProtocolException {
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Upload-Offset", "3")
.withHeader("Content-Type", "application/offset+octet-stream")
.withHeader("Connection", "keep-alive")
.withBody(Arrays.copyOfRange(content, 3, 11)))
.respond(new HttpResponse()
.withStatusCode(204)
Expand All @@ -70,6 +74,43 @@ public void testTusUploader() throws IOException, ProtocolException {
uploader.finish();
}

/**
* Tests if the {@link TusUploader} actually uploads files through a proxy.
* @throws IOException
* @throws ProtocolException
*/
@Test
public void testTusUploaderWithProxy() throws IOException, ProtocolException {
byte[] content = "hello world with proxy".getBytes();

mockServer.when(new HttpRequest()
.withPath("/files/foo")
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Upload-Offset", "0")
.withHeader("Content-Type", "application/offset+octet-stream")
.withHeader("Proxy-Connection", "keep-alive")
.withBody(Arrays.copyOf(content, content.length)))
.respond(new HttpResponse()
.withStatusCode(204)
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
.withHeader("Upload-Offset", "22"));

TusClient client = new TusClient();
URL uploadUrl = new URL(mockServerURL + "/foo");
Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress("localhost", mockServer.getPort()));
TusInputStream input = new TusInputStream(new ByteArrayInputStream(content));
long offset = 0;

TusUpload upload = new TusUpload();

TusUploader uploader = new TusUploader(client, upload, uploadUrl, input, offset);
uploader.setProxy(proxy);

assertEquals(proxy, uploader.getProxy());
assertEquals(22, uploader.uploadChunk());
uploader.finish();
}

/**
* Verifies, that {@link TusClient#uploadFinished(TusUpload)} gets called after a proper upload has been finished.
* @throws IOException
Expand Down