Skip to content

Commit afe0453

Browse files
kayhubaaiham
authored andcommitted
Add proxy authentication support, fixes #102 (#133)
* [#102] HTTP Proxy supports basic auth * [#102] Proxy config allows auth scheme selection * [#102] JDK8 is now required In order to use AHC version 2.0+ JDK8 is the minimum requirement. * [#102] Proxy config allows auth scheme selection Added Kerberos and SPNEGO authentication schemes as well. * [#102] Travis build no longer tries with JDK7 * [#102] README.md updated to indicate JDK8 req * [#102] Updated travis.yml, removed double JDK8 * [#102] Updated travis.yml,removed "dist:precise" * [#102] Removed IOException for listArchives
1 parent d4bdc5a commit afe0453

File tree

5 files changed

+107
-33
lines changed

5 files changed

+107
-33
lines changed

.travis.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@ dist: trusty
22
language: java
33
matrix:
44
include:
5-
- dist: precise
6-
jdk: openjdk7
7-
- dist: precise
8-
jdk: oraclejdk7
9-
- jdk: openjdk8
105
- jdk: oraclejdk8
6+
- jdk: openjdk8
117

128
notifications:
139
slack:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ Reference documentation is available at
255255
You need an OpenTok API key and API secret, which you can obtain by logging into your
256256
[TokBox account](https://tokbox.com/account).
257257

258-
The OpenTok Java SDK requires JDK 6 or greater to compile. Runtime requires Java SE 6 or greater.
258+
The OpenTok Java SDK requires JDK 8 or greater to compile. Runtime requires Java SE 8 or greater.
259259
This project is tested on both OpenJDK and Oracle implementations.
260260

261261

build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ dependencies {
4848
testCompile group: 'junit', name: 'junit', version: '[4.3,5.0['
4949
testCompile group: 'com.github.tomakehurst', name: 'wiremock', version: '[1.45,1.99999)'
5050
compile group: 'commons-lang', name: 'commons-lang', version: '[2.6,2.99999)'
51-
compile group: 'com.ning', name: 'async-http-client', version: '[1.9,2.0['
51+
compile group: 'org.asynchttpclient', name: 'async-http-client', version: '2.0.34'
5252
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '[2.3.1,2.99999)'
5353
compile group: 'commons-validator', name: 'commons-validator', version: '[1.4.0,1.99999)'
5454
compile group: 'commons-codec', name: 'commons-codec', version: '[1.9,1.99999]'
@@ -130,14 +130,14 @@ test {
130130
maxHeapSize = "1024m"
131131
}
132132

133-
sourceCompatibility = 1.7
133+
sourceCompatibility = 1.8
134134
// credit: http://stackoverflow.com/a/22681854/305340
135135
tasks.withType(JavaCompile) {
136136
options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked"
137137
doFirst {
138-
if (System.env.JDK7_HOME != null) {
139-
options.bootClasspath = "$System.env.JDK7_HOME/jre/lib/rt.jar"
140-
options.bootClasspath += "$File.pathSeparator$System.env.JDK7_HOME/jre/lib/jce.jar"
138+
if (System.env.JDK8_HOME != null) {
139+
options.bootClasspath = "$System.env.JDK8_HOME/jre/lib/rt.jar"
140+
options.bootClasspath += "$File.pathSeparator$System.env.JDK8_HOME/jre/lib/jce.jar"
141141
// and other specific JDK jars
142142
}
143143
}

src/main/java/com/opentok/OpenTok.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.opentok.exception.RequestException;
1616
import com.opentok.util.Crypto;
1717
import com.opentok.util.HttpClient;
18+
import com.opentok.util.HttpClient.ProxyAuthScheme;
1819

1920
import java.io.IOException;
2021
import java.io.UnsupportedEncodingException;
@@ -438,6 +439,9 @@ public static class Builder {
438439
private String apiSecret;
439440
private String apiUrl;
440441
private Proxy proxy;
442+
private ProxyAuthScheme proxyAuthScheme;
443+
private String principal;
444+
private String password;
441445

442446
public Builder(int apiKey, String apiSecret) {
443447
this.apiKey = apiKey;
@@ -450,7 +454,15 @@ public Builder apiUrl(String apiUrl) {
450454
}
451455

452456
public Builder proxy(Proxy proxy) {
457+
proxy(proxy, null, null, null);
458+
return this;
459+
}
460+
461+
public Builder proxy(Proxy proxy, ProxyAuthScheme proxyAuthScheme, String principal, String password) {
453462
this.proxy = proxy;
463+
this.proxyAuthScheme = proxyAuthScheme;
464+
this.principal = principal;
465+
this.password = password;
454466
return this;
455467
}
456468

@@ -461,7 +473,7 @@ public OpenTok build() {
461473
clientBuilder.apiUrl(this.apiUrl);
462474
}
463475
if (this.proxy != null) {
464-
clientBuilder.proxy(this.proxy);
476+
clientBuilder.proxy(this.proxy, proxyAuthScheme, principal, password);
465477
}
466478

467479
return new OpenTok(this.apiKey, this.apiSecret, clientBuilder.build());

src/main/java/com/opentok/util/HttpClient.java

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,38 @@
1111
import java.net.InetSocketAddress;
1212
import java.net.Proxy;
1313
import java.net.SocketAddress;
14+
import java.util.ArrayList;
1415
import java.util.Collection;
16+
import java.util.HashMap;
17+
import java.util.List;
1518
import java.util.Map;
19+
import java.util.Map.Entry;
1620
import java.util.concurrent.ExecutionException;
1721
import java.util.concurrent.Future;
1822

23+
import org.asynchttpclient.AsyncHttpClientConfig;
24+
import org.asynchttpclient.DefaultAsyncHttpClient;
25+
import org.asynchttpclient.DefaultAsyncHttpClientConfig;
26+
import org.asynchttpclient.Realm;
27+
import org.asynchttpclient.Realm.AuthScheme;
28+
import org.asynchttpclient.RequestBuilder;
29+
import org.asynchttpclient.Response;
30+
import org.asynchttpclient.filter.FilterContext;
31+
import org.asynchttpclient.filter.FilterException;
32+
import org.asynchttpclient.filter.RequestFilter;
33+
import org.asynchttpclient.proxy.ProxyServer;
34+
1935
import com.fasterxml.jackson.core.JsonProcessingException;
2036
import com.fasterxml.jackson.databind.ObjectMapper;
2137
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
2238
import com.fasterxml.jackson.databind.node.ObjectNode;
23-
import com.ning.http.client.*;
24-
import com.ning.http.client.filter.FilterContext;
25-
import com.ning.http.client.filter.FilterException;
26-
import com.ning.http.client.filter.RequestFilter;
2739
import com.opentok.ArchiveProperties;
2840
import com.opentok.constants.DefaultApiUrl;
2941
import com.opentok.constants.Version;
3042
import com.opentok.exception.OpenTokException;
3143
import com.opentok.exception.RequestException;
3244

33-
public class HttpClient extends AsyncHttpClient {
45+
public class HttpClient extends DefaultAsyncHttpClient {
3446

3547
private final String apiUrl;
3648
private final int apiKey;
@@ -44,10 +56,16 @@ private HttpClient(Builder builder) {
4456
public String createSession(Map<String, Collection<String>> params) throws RequestException {
4557
String responseString = null;
4658
Response response = null;
47-
FluentStringsMap paramsString = new FluentStringsMap().addAll(params);
59+
Map<String, List<String>> paramsWithList = null;
60+
if (params != null) {
61+
paramsWithList = new HashMap<>();
62+
for (Entry<String, Collection<String>> entry : params.entrySet()) {
63+
paramsWithList.put(entry.getKey(), new ArrayList<>(entry.getValue()));
64+
}
65+
}
4866

4967
Future<Response> request = this.preparePost(this.apiUrl + "/session/create")
50-
.setFormParams(paramsString)
68+
.setFormParams(paramsWithList)
5169
.addHeader("Accept", "application/json") // XML version is deprecated
5270
.execute();
5371

@@ -61,7 +79,7 @@ public String createSession(Map<String, Collection<String>> params) throws Reque
6179
throw new RequestException("Could not create an OpenTok Session. The server response was invalid." +
6280
" response code: " + response.getStatusCode());
6381
}
64-
} catch (InterruptedException | ExecutionException | IOException e) {
82+
} catch (InterruptedException | ExecutionException e) {
6583
throw new RequestException("Could not create an OpenTok Session", e);
6684
}
6785
return responseString;
@@ -89,7 +107,7 @@ public String getArchive(String archiveId) throws RequestException {
89107
throw new RequestException("Could not get an OpenTok Archive. The server response was invalid." +
90108
" response code: " + response.getStatusCode());
91109
}
92-
} catch (InterruptedException | ExecutionException | IOException e) {
110+
} catch (InterruptedException | ExecutionException e) {
93111
throw new RequestException("Could not get an OpenTok Archive", e);
94112
}
95113

@@ -126,7 +144,7 @@ public String getArchives(int offset, int count) throws RequestException {
126144
throw new RequestException("Could not get an OpenTok Archive. The server response was invalid." +
127145
" response code: " + response.getStatusCode());
128146
}
129-
} catch (InterruptedException | ExecutionException | IOException e) {
147+
} catch (InterruptedException | ExecutionException e) {
130148
throw new RequestException("Could not get OpenTok Archives", e);
131149
}
132150

@@ -150,7 +168,7 @@ public String getArchives(String sessionId) throws RequestException {
150168
throw new RequestException("Could not get an OpenTok Archive. The server response was invalid."
151169
+ " response code: " + response.getStatusCode());
152170
}
153-
} catch (InterruptedException | ExecutionException | IOException e) {
171+
} catch (InterruptedException | ExecutionException e) {
154172
throw new RequestException("Could not get OpenTok Archives", e);
155173
}
156174
}
@@ -202,7 +220,7 @@ public String startArchive(String sessionId, ArchiveProperties properties)
202220
throw new RequestException("Could not start an OpenTok Archive. The server response was invalid." +
203221
" response code: " + response.getStatusCode());
204222
}
205-
} catch (InterruptedException | ExecutionException | IOException e) {
223+
} catch (InterruptedException | ExecutionException e) {
206224
throw new RequestException("Could not start an OpenTok Archive.", e);
207225
}
208226
return responseString;
@@ -238,7 +256,7 @@ public String stopArchive(String archiveId) throws RequestException {
238256
throw new RequestException("Could not stop an OpenTok Archive. The server response was invalid." +
239257
" response code: " + response.getStatusCode());
240258
}
241-
} catch (InterruptedException | ExecutionException | IOException e) {
259+
} catch (InterruptedException | ExecutionException e) {
242260
throw new RequestException("Could not stop an OpenTok Archive.", e);
243261
}
244262
return responseString;
@@ -266,17 +284,28 @@ public String deleteArchive(String archiveId) throws RequestException {
266284
throw new RequestException("Could not get an OpenTok Archive. The server response was invalid." +
267285
" response code: " + response.getStatusCode());
268286
}
269-
} catch (InterruptedException | ExecutionException | IOException e) {
287+
} catch (InterruptedException | ExecutionException e) {
270288
throw new RequestException("Could not delete an OpenTok Archive. archiveId = " + archiveId, e);
271289
}
272290

273291
return responseString;
274292
}
293+
294+
public static enum ProxyAuthScheme {
295+
BASIC,
296+
DIGEST,
297+
NTLM,
298+
SPNEGO,
299+
KERBEROS
300+
}
275301

276302
public static class Builder {
277303
private final int apiKey;
278304
private final String apiSecret;
279305
private Proxy proxy;
306+
private ProxyAuthScheme proxyAuthScheme;
307+
private String principal;
308+
private String password;
280309
private String apiUrl;
281310
private AsyncHttpClientConfig config;
282311

@@ -291,20 +320,28 @@ public Builder apiUrl(String apiUrl) {
291320
}
292321

293322
public Builder proxy(Proxy proxy) {
323+
proxy(proxy, null, null, null);
324+
return this;
325+
}
326+
327+
public Builder proxy(Proxy proxy, ProxyAuthScheme proxyAuthScheme, String principal, String password) {
294328
this.proxy = proxy;
329+
this.proxyAuthScheme = proxyAuthScheme;
330+
this.principal = principal;
331+
this.password = password;
295332
return this;
296333
}
297334

298335
public HttpClient build() {
299-
AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder()
336+
DefaultAsyncHttpClientConfig.Builder configBuilder = new DefaultAsyncHttpClientConfig.Builder()
300337
.setUserAgent("Opentok-Java-SDK/" + Version.VERSION + " JRE/" + System.getProperty("java.version"))
301338
.addRequestFilter(new TokenAuthRequestFilter(this.apiKey, this.apiSecret));
302339
if (this.apiUrl == null) {
303340
this.apiUrl=DefaultApiUrl.DEFAULT_API_URI;
304341
}
305342

306343
if (this.proxy != null) {
307-
configBuilder.setProxyServer(createProxyServer(this.proxy));
344+
configBuilder.setProxyServer(createProxyServer(this.proxy, this.proxyAuthScheme, this.principal, this.password));
308345
}
309346

310347
this.config = configBuilder.build();
@@ -314,7 +351,7 @@ public HttpClient build() {
314351
}
315352

316353
// credit: https://github.com/AsyncHttpClient/async-http-client/blob/b52a8de5d6a862b5d1652d62f87ce774cbcff156/src/main/java/com/ning/http/client/ProxyServer.java#L99-L127
317-
static ProxyServer createProxyServer(final Proxy proxy) {
354+
static ProxyServer createProxyServer(final Proxy proxy, ProxyAuthScheme proxyAuthScheme, String principal, String password) {
318355
switch (proxy.type()) {
319356
case DIRECT:
320357
return null;
@@ -329,9 +366,37 @@ static ProxyServer createProxyServer(final Proxy proxy) {
329366
}
330367

331368
InetSocketAddress isa = (InetSocketAddress) sa;
332-
369+
333370
final String isaHost = isa.isUnresolved() ? isa.getHostName() : isa.getAddress().getHostAddress();
334-
return new ProxyServer(isaHost, isa.getPort());
371+
ProxyServer.Builder builder = new ProxyServer.Builder(isaHost, isa.getPort());
372+
373+
if (principal != null) {
374+
Realm.AuthScheme authScheme = null;
375+
switch (proxyAuthScheme) {
376+
case BASIC:
377+
authScheme = AuthScheme.BASIC;
378+
break;
379+
case DIGEST:
380+
authScheme = AuthScheme.DIGEST;
381+
break;
382+
case NTLM:
383+
authScheme = AuthScheme.NTLM;
384+
break;
385+
case KERBEROS:
386+
authScheme = AuthScheme.KERBEROS;
387+
break;
388+
case SPNEGO:
389+
authScheme = AuthScheme.SPNEGO;
390+
break;
391+
}
392+
393+
Realm.Builder rb = new Realm.Builder(principal, password);
394+
rb.setScheme(authScheme);
395+
396+
builder.setRealm(rb.build());
397+
}
398+
399+
return builder.build();
335400
}
336401
}
337402

@@ -346,9 +411,10 @@ public TokenAuthRequestFilter(int apiKey, String apiSecret) {
346411
this.apiSecret = apiSecret;
347412
}
348413

349-
public FilterContext filter(FilterContext ctx) throws FilterException {
414+
@Override
415+
public <T> FilterContext<T> filter(FilterContext<T> ctx) throws FilterException {
350416
try {
351-
return new FilterContext.FilterContextBuilder(ctx)
417+
return new FilterContext.FilterContextBuilder<T>(ctx)
352418
.request(new RequestBuilder(ctx.getRequest())
353419
.addHeader(authHeader, TokenGenerator.generateToken(apiKey, apiSecret))
354420
.build())

0 commit comments

Comments
 (0)