Skip to content

Commit 67de05c

Browse files
authored
Add optional proxy to tus client & uploader (#84)
* Add optional proxy to tus client & uploader By default the NO_PROXY proxy is used which is the same as a direct request. Adding the possibility to override the proxy with an own instance pointing to the proxy to use when uploading. * Fix PR review comments for adding optional proxy
1 parent fb79d40 commit 67de05c

File tree

5 files changed

+187
-6
lines changed

5 files changed

+187
-6
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ public void prepareConnection(@NotNull HttpURLConnection connection) {
118118
}
119119
```
120120

121+
### Can I use a proxy that will be used for uploading files?
122+
123+
Yes, just add a proxy to the TusClient as shown below (1 line added to the above [usage](#usage)):
124+
125+
```java
126+
TusClient client = new TusClient();
127+
Proxy myProxy = new Proxy(...);
128+
client.setProxy(myProxy);
129+
```
130+
121131
## License
122132

123133
MIT

src/main/java/io/tus/java/client/TusClient.java

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.tus.java.client;
22

3+
import java.net.Proxy;
34
import org.jetbrains.annotations.NotNull;
45
import org.jetbrains.annotations.Nullable;
56

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

2122
private URL uploadCreationURL;
23+
private Proxy proxy;
2224
private boolean resumingEnabled;
2325
private boolean removeFingerprintOnSuccessEnabled;
2426
private TusURLStore urlStore;
@@ -52,6 +54,24 @@ public URL getUploadCreationURL() {
5254
return uploadCreationURL;
5355
}
5456

57+
/**
58+
* Set the proxy that will be used for all requests.
59+
*
60+
* @param proxy Proxy to use
61+
*/
62+
public void setProxy(Proxy proxy) {
63+
this.proxy = proxy;
64+
}
65+
66+
/**
67+
* Get the current proxy used for all requests.
68+
*
69+
* @return Current proxy
70+
*/
71+
public Proxy getProxy() {
72+
return proxy;
73+
}
74+
5575
/**
5676
* Enable resuming already started uploads. This step is required if you want to use
5777
* {@link #resumeUpload(TusUpload)}.
@@ -174,7 +194,7 @@ public int getConnectTimeout() {
174194
* @throws IOException Thrown if an exception occurs while issuing the HTTP request.
175195
*/
176196
public TusUploader createUpload(@NotNull TusUpload upload) throws ProtocolException, IOException {
177-
HttpURLConnection connection = (HttpURLConnection) uploadCreationURL.openConnection();
197+
HttpURLConnection connection = openConnection(uploadCreationURL);
178198
connection.setRequestMethod("POST");
179199
prepareConnection(connection);
180200

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

209-
return new TusUploader(this, upload, uploadURL, upload.getTusInputStream(), 0);
229+
return createUploader(upload, uploadURL, 0L);
230+
}
231+
232+
@NotNull
233+
private HttpURLConnection openConnection(@NotNull URL uploadURL) throws IOException {
234+
if (proxy != null) {
235+
return (HttpURLConnection) uploadURL.openConnection(proxy);
236+
}
237+
return (HttpURLConnection) uploadURL.openConnection();
238+
}
239+
240+
@NotNull
241+
private TusUploader createUploader(@NotNull TusUpload upload, @NotNull URL uploadURL, long offset)
242+
throws IOException {
243+
TusUploader uploader = new TusUploader(this, upload, uploadURL, upload.getTusInputStream(), offset);
244+
uploader.setProxy(proxy);
245+
return uploader;
210246
}
211247

212248
/**
@@ -259,7 +295,7 @@ public TusUploader resumeUpload(@NotNull TusUpload upload) throws
259295
*/
260296
public TusUploader beginOrResumeUploadFromURL(@NotNull TusUpload upload, @NotNull URL uploadURL) throws
261297
ProtocolException, IOException {
262-
HttpURLConnection connection = (HttpURLConnection) uploadURL.openConnection();
298+
HttpURLConnection connection = openConnection(uploadURL);
263299
connection.setRequestMethod("HEAD");
264300
prepareConnection(connection);
265301

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

280-
return new TusUploader(this, upload, uploadURL, upload.getTusInputStream(), offset);
316+
return createUploader(upload, uploadURL, offset);
281317
}
282318

283319
/**

src/main/java/io/tus/java/client/TusUploader.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.IOException;
44
import java.io.OutputStream;
55
import java.net.HttpURLConnection;
6+
import java.net.Proxy;
67
import java.net.URL;
78
import java.net.URLConnection;
89

@@ -21,6 +22,7 @@
2122
*/
2223
public class TusUploader {
2324
private URL uploadURL;
25+
private Proxy proxy;
2426
private TusInputStream input;
2527
private long offset;
2628
private TusClient client;
@@ -44,7 +46,7 @@ public class TusUploader {
4446
* @throws IOException Thrown if an exception occurs while issuing the HTTP request.
4547
*/
4648
public TusUploader(TusClient client, TusUpload upload, URL uploadURL, TusInputStream input, long offset)
47-
throws IOException {
49+
throws IOException {
4850
this.uploadURL = uploadURL;
4951
this.input = input;
5052
this.offset = offset;
@@ -65,7 +67,11 @@ private void openConnection() throws IOException, ProtocolException {
6567
bytesRemainingForRequest = requestPayloadSize;
6668
input.mark(requestPayloadSize);
6769

68-
connection = (HttpURLConnection) uploadURL.openConnection();
70+
if (proxy != null) {
71+
connection = (HttpURLConnection) uploadURL.openConnection(proxy);
72+
} else {
73+
connection = (HttpURLConnection) uploadURL.openConnection();
74+
}
6975
client.prepareConnection(connection);
7076
connection.setRequestProperty("Upload-Offset", Long.toString(offset));
7177
connection.setRequestProperty("Content-Type", "application/offset+octet-stream");
@@ -268,6 +274,24 @@ public URL getUploadURL() {
268274
return uploadURL;
269275
}
270276

277+
/**
278+
* Set the proxy that will be used when uploading.
279+
*
280+
* @param proxy Proxy to use
281+
*/
282+
public void setProxy(Proxy proxy) {
283+
this.proxy = proxy;
284+
}
285+
286+
/**
287+
* This methods returns the proxy used when uploading.
288+
*
289+
* @return The {@link Proxy} used for the upload or null when not set.
290+
*/
291+
public Proxy getProxy() {
292+
return proxy;
293+
}
294+
271295
/**
272296
* Finish the request by closing the HTTP connection and the InputStream.
273297
* You can call this method even before the entire file has been uploaded. Use this behavior to

src/test/java/io/tus/java/client/TestTusClient.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
import java.io.ByteArrayInputStream;
99
import java.io.IOException;
1010
import java.net.HttpURLConnection;
11+
import java.net.InetSocketAddress;
1112
import java.net.MalformedURLException;
13+
import java.net.Proxy;
14+
import java.net.Proxy.Type;
1215
import java.net.URL;
1316
import java.util.HashMap;
1417
import java.util.LinkedHashMap;
@@ -80,6 +83,7 @@ public void testCreateUpload() throws IOException, ProtocolException {
8083
mockServer.when(new HttpRequest()
8184
.withMethod("POST")
8285
.withPath("/files")
86+
.withHeader("Connection", "keep-alive")
8387
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
8488
.withHeader("Upload-Metadata", "foo aGVsbG8=,bar d29ybGQ=")
8589
.withHeader("Upload-Length", "10"))
@@ -102,6 +106,40 @@ public void testCreateUpload() throws IOException, ProtocolException {
102106

103107
assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));
104108
}
109+
/**
110+
* Verifies if uploads can be created with the tus client through a proxy.
111+
* @throws IOException if upload data cannot be read.
112+
* @throws ProtocolException if the upload cannot be constructed.
113+
*/
114+
@Test
115+
public void testCreateUploadWithProxy() throws IOException, ProtocolException {
116+
mockServer.when(new HttpRequest()
117+
.withMethod("POST")
118+
.withPath("/files")
119+
.withHeader("Proxy-Connection", "keep-alive")
120+
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
121+
.withHeader("Upload-Metadata", "foo aGVsbG8=,bar d29ybGQ=")
122+
.withHeader("Upload-Length", "11"))
123+
.respond(new HttpResponse()
124+
.withStatusCode(201)
125+
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
126+
.withHeader("Location", mockServerURL + "/foo"));
127+
128+
Map<String, String> metadata = new LinkedHashMap<String, String>();
129+
metadata.put("foo", "hello");
130+
metadata.put("bar", "world");
131+
132+
TusClient client = new TusClient();
133+
client.setUploadCreationURL(mockServerURL);
134+
client.setProxy(new Proxy(Type.HTTP, new InetSocketAddress("localhost", mockServer.getPort())));
135+
TusUpload upload = new TusUpload();
136+
upload.setSize(11);
137+
upload.setInputStream(new ByteArrayInputStream(new byte[11]));
138+
upload.setMetadata(metadata);
139+
TusUploader uploader = client.createUpload(upload);
140+
141+
assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));
142+
}
105143

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

298+
/**
299+
* Tests if an upload gets started when {@link TusClient#resumeOrCreateUpload(TusUpload)} gets called with
300+
* a proxy set.
301+
* @throws IOException
302+
* @throws ProtocolException
303+
*/
304+
@Test
305+
public void testResumeOrCreateUploadWithProxy() throws IOException, ProtocolException {
306+
mockServer.when(new HttpRequest()
307+
.withMethod("POST")
308+
.withPath("/files")
309+
.withHeader("Proxy-Connection", "keep-alive")
310+
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
311+
.withHeader("Upload-Length", "11"))
312+
.respond(new HttpResponse()
313+
.withStatusCode(201)
314+
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
315+
.withHeader("Location", mockServerURL + "/foo"));
316+
317+
TusClient client = new TusClient();
318+
client.setUploadCreationURL(mockServerURL);
319+
Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress("localhost", mockServer.getPort()));
320+
client.setProxy(proxy);
321+
TusUpload upload = new TusUpload();
322+
upload.setSize(11);
323+
upload.setInputStream(new ByteArrayInputStream(new byte[11]));
324+
TusUploader uploader = client.resumeOrCreateUpload(upload);
325+
326+
assertEquals(proxy, client.getProxy());
327+
assertEquals(uploader.getUploadURL(), new URL(mockServerURL + "/foo"));
328+
}
259329

260330
/**
261331
* Checks if a new upload attempt is started in case of a serverside 404-error, without having an Exception thrown.

src/test/java/io/tus/java/client/TestTusUploader.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
import java.io.IOException;
1212
import java.io.InputStreamReader;
1313
import java.io.OutputStream;
14+
import java.net.InetSocketAddress;
1415
import java.net.MalformedURLException;
16+
import java.net.Proxy;
17+
import java.net.Proxy.Type;
1518
import java.net.ServerSocket;
1619
import java.net.Socket;
1720
import java.net.URL;
@@ -44,6 +47,7 @@ public void testTusUploader() throws IOException, ProtocolException {
4447
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
4548
.withHeader("Upload-Offset", "3")
4649
.withHeader("Content-Type", "application/offset+octet-stream")
50+
.withHeader("Connection", "keep-alive")
4751
.withBody(Arrays.copyOfRange(content, 3, 11)))
4852
.respond(new HttpResponse()
4953
.withStatusCode(204)
@@ -70,6 +74,43 @@ public void testTusUploader() throws IOException, ProtocolException {
7074
uploader.finish();
7175
}
7276

77+
/**
78+
* Tests if the {@link TusUploader} actually uploads files through a proxy.
79+
* @throws IOException
80+
* @throws ProtocolException
81+
*/
82+
@Test
83+
public void testTusUploaderWithProxy() throws IOException, ProtocolException {
84+
byte[] content = "hello world with proxy".getBytes();
85+
86+
mockServer.when(new HttpRequest()
87+
.withPath("/files/foo")
88+
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
89+
.withHeader("Upload-Offset", "0")
90+
.withHeader("Content-Type", "application/offset+octet-stream")
91+
.withHeader("Proxy-Connection", "keep-alive")
92+
.withBody(Arrays.copyOf(content, content.length)))
93+
.respond(new HttpResponse()
94+
.withStatusCode(204)
95+
.withHeader("Tus-Resumable", TusClient.TUS_VERSION)
96+
.withHeader("Upload-Offset", "22"));
97+
98+
TusClient client = new TusClient();
99+
URL uploadUrl = new URL(mockServerURL + "/foo");
100+
Proxy proxy = new Proxy(Type.HTTP, new InetSocketAddress("localhost", mockServer.getPort()));
101+
TusInputStream input = new TusInputStream(new ByteArrayInputStream(content));
102+
long offset = 0;
103+
104+
TusUpload upload = new TusUpload();
105+
106+
TusUploader uploader = new TusUploader(client, upload, uploadUrl, input, offset);
107+
uploader.setProxy(proxy);
108+
109+
assertEquals(proxy, uploader.getProxy());
110+
assertEquals(22, uploader.uploadChunk());
111+
uploader.finish();
112+
}
113+
73114
/**
74115
* Verifies, that {@link TusClient#uploadFinished(TusUpload)} gets called after a proper upload has been finished.
75116
* @throws IOException

0 commit comments

Comments
 (0)