Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit 18b86cf

Browse files
authored
Merge pull request #4 from launchdarkly/dr/proxyAuth
Add proxy authentication support
2 parents 08c0895 + a8ea8e3 commit 18b86cf

File tree

5 files changed

+123
-10
lines changed

5 files changed

+123
-10
lines changed

CONTRIBUTING.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,25 @@
11
Contributing to the LaunchDarkly SDK for Java
22
================================================
33

4-
We encourage pull-requests and other contributions from the community. We've also published an [SDK contributor's guide](http://docs.launchdarkly.com/v1.0/docs/sdk-contributors-guide) that provides a detailed explanation of how our SDKs work.
4+
We encourage pull-requests and other contributions from the community. We've also published an [SDK contributor's guide](http://docs.launchdarkly.com/v1.0/docs/sdk-contributors-guide) that provides a detailed explanation of how our SDKs work.
5+
6+
7+
Testing Proxy Settings
8+
==================
9+
Installation is your own journey, but your squid.conf file should have auth/access sections that look something like this:
10+
11+
```
12+
auth_param basic program /usr/local/Cellar/squid/3.5.6/libexec/basic_ncsa_auth <SQUID_DIR>/passwords
13+
auth_param basic realm proxy
14+
acl authenticated proxy_auth REQUIRED
15+
http_access allow authenticated
16+
# And finally deny all other access to this proxy
17+
http_access deny all
18+
```
19+
20+
The contents of the passwords file is:
21+
```
22+
user:$apr1$sBfNiLFJ$7h3S84EgJhlbWM3v.90v61
23+
```
24+
25+
The username/password is: user/password

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dependencies {
3030
compile "com.google.guava:guava:19.0"
3131
compile "joda-time:joda-time:2.9.3"
3232
compile "org.slf4j:slf4j-api:1.7.21"
33-
compile group: "com.launchdarkly", name: "okhttp-eventsource", version: "1.2.0-SNAPSHOT", changing: true
33+
compile group: "com.launchdarkly", name: "okhttp-eventsource", version: "1.3.0", changing: true
3434
compile "redis.clients:jedis:2.9.0"
3535
testCompile "org.easymock:easymock:3.4"
3636
testCompile 'junit:junit:4.12'

src/main/java/com/launchdarkly/client/LDConfig.java

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22

33
import com.google.common.io.Files;
44
import com.google.gson.Gson;
5+
import okhttp3.Authenticator;
56
import okhttp3.Cache;
67
import okhttp3.ConnectionPool;
8+
import okhttp3.Credentials;
79
import okhttp3.OkHttpClient;
810
import okhttp3.Request;
11+
import okhttp3.Response;
12+
import okhttp3.Route;
913
import org.slf4j.Logger;
1014
import org.slf4j.LoggerFactory;
1115

1216
import java.io.File;
17+
import java.io.IOException;
1318
import java.net.InetSocketAddress;
1419
import java.net.Proxy;
1520
import java.net.URI;
@@ -46,6 +51,7 @@ public final class LDConfig {
4651
final int socketTimeoutMillis;
4752
final int flushInterval;
4853
final Proxy proxy;
54+
final Authenticator proxyAuthenticator;
4955
final OkHttpClient httpClient;
5056
final boolean stream;
5157
final FeatureStore featureStore;
@@ -64,6 +70,7 @@ protected LDConfig(Builder builder) {
6470
this.socketTimeoutMillis = builder.socketTimeoutMillis;
6571
this.flushInterval = builder.flushIntervalSeconds;
6672
this.proxy = builder.proxy();
73+
this.proxyAuthenticator = builder.proxyAuthenticator();
6774
this.streamURI = builder.streamURI;
6875
this.stream = builder.stream;
6976
this.featureStore = builder.featureStore;
@@ -91,12 +98,24 @@ protected LDConfig(Builder builder) {
9198

9299
if (proxy != null) {
93100
httpClientBuilder.proxy(proxy);
101+
if (proxyAuthenticator != null) {
102+
httpClientBuilder.proxyAuthenticator(proxyAuthenticator);
103+
logger.info("Using proxy: " + proxy + " with authentication.");
104+
} else {
105+
logger.info("Using proxy: " + proxy + " without authentication.");
106+
}
94107
}
95108

96109
httpClient = httpClientBuilder
97110
.build();
98111
}
99112

113+
Request.Builder getRequestBuilder(String sdkKey) {
114+
return new Request.Builder()
115+
.addHeader("Authorization", sdkKey)
116+
.addHeader("User-Agent", "JavaClient/" + LDClient.CLIENT_VERSION);
117+
}
118+
100119
/**
101120
* A <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a> that helps construct {@link com.launchdarkly.client.LDConfig} objects. Builder
102121
* calls can be chained, enabling the following pattern:
@@ -118,6 +137,8 @@ public static class Builder {
118137
private int flushIntervalSeconds = DEFAULT_FLUSH_INTERVAL_SECONDS;
119138
private String proxyHost = "localhost";
120139
private int proxyPort = -1;
140+
private String proxyUsername = null;
141+
private String proxyPassword = null;
121142
private boolean stream = true;
122143
private boolean useLdd = false;
123144
private boolean offline = false;
@@ -289,6 +310,30 @@ public Builder proxyPort(int port) {
289310
return this;
290311
}
291312

313+
/**
314+
* Sets the username for the optional HTTP proxy. Only used when {@link LDConfig.Builder#proxyPassword(String)}
315+
* is also called.
316+
*
317+
* @param username
318+
* @return the builder
319+
*/
320+
public Builder proxyUsername(String username) {
321+
this.proxyUsername = username;
322+
return this;
323+
}
324+
325+
/**
326+
* Sets the password for the optional HTTP proxy. Only used when {@link LDConfig.Builder#proxyUsername(String)}
327+
* is also called.
328+
*
329+
* @param password
330+
* @return the builder
331+
*/
332+
public Builder proxyPassword(String password) {
333+
this.proxyPassword = password;
334+
return this;
335+
}
336+
292337
/**
293338
* Deprecated. Only HTTP proxies are currently supported.
294339
*
@@ -352,7 +397,7 @@ public Builder startWaitMillis(long startWaitMillis) {
352397
* Enable event sampling. When set to the default of zero, sampling is disabled and all events
353398
* are sent back to LaunchDarkly. When set to greater than zero, there is a 1 in
354399
* <code>samplingInterval</code> chance events will be will be sent.
355-
*
400+
* <p>
356401
* <p>Example: if you want 5% sampling rate, set <code>samplingInterval</code> to 20.
357402
*
358403
* @param samplingInterval the sampling interval.
@@ -386,6 +431,24 @@ Proxy proxy() {
386431
}
387432
}
388433

434+
Authenticator proxyAuthenticator() {
435+
if (this.proxyUsername != null && this.proxyPassword != null) {
436+
final String credential = Credentials.basic(proxyUsername, proxyPassword);
437+
return new Authenticator() {
438+
public Request authenticate(Route route, Response response) throws IOException {
439+
if (response.request().header("Proxy-Authorization") != null) {
440+
return null; // Give up, we've already failed to authenticate with the proxy.
441+
} else {
442+
return response.request().newBuilder()
443+
.header("Proxy-Authorization", credential)
444+
.build();
445+
}
446+
}
447+
};
448+
}
449+
return null;
450+
}
451+
389452
/**
390453
* Build the configured {@link com.launchdarkly.client.LDConfig} object
391454
*
@@ -394,12 +457,5 @@ Proxy proxy() {
394457
public LDConfig build() {
395458
return new LDConfig(this);
396459
}
397-
398-
}
399-
400-
Request.Builder getRequestBuilder(String sdkKey) {
401-
return new Request.Builder()
402-
.addHeader("Authorization", sdkKey)
403-
.addHeader("User-Agent", "JavaClient/" + LDClient.CLIENT_VERSION);
404460
}
405461
}

src/main/java/com/launchdarkly/client/StreamProcessor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ public void onError(Throwable throwable) {
135135

136136
if (config.proxy != null) {
137137
builder.proxy(config.proxy);
138+
if (config.proxyAuthenticator != null) {
139+
builder.proxyAuthenticator(config.proxyAuthenticator);
140+
}
138141
}
139142

140143
es = builder.build();

src/test/java/com/launchdarkly/client/LDConfigTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.net.Proxy;
77

88
import static org.junit.Assert.assertEquals;
9+
import static org.junit.Assert.assertNotNull;
910
import static org.junit.Assert.assertNull;
1011

1112
public class LDConfigTest {
@@ -40,6 +41,7 @@ public void testSocketTimeoutSpecifiedInMilliseconds() {
4041
public void testNoProxyConfigured() {
4142
LDConfig config = new LDConfig.Builder().build();
4243
assertNull(config.proxy);
44+
assertNull(config.proxyAuthenticator);
4345
}
4446

4547
@Test
@@ -62,6 +64,37 @@ public void testProxy() {
6264
assertEquals(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost2", 4444)), config.proxy);
6365
}
6466

67+
@Test
68+
public void testProxyAuth() {
69+
LDConfig config = new LDConfig.Builder()
70+
.proxyHost("localhost2")
71+
.proxyPort(4444)
72+
.proxyUsername("proxyUser")
73+
.proxyPassword("proxyPassword")
74+
.build();
75+
assertNotNull(config.proxy);
76+
assertNotNull(config.proxyAuthenticator);
77+
}
78+
79+
@Test
80+
public void testProxyAuthPartialConfig() {
81+
LDConfig config = new LDConfig.Builder()
82+
.proxyHost("localhost2")
83+
.proxyPort(4444)
84+
.proxyUsername("proxyUser")
85+
.build();
86+
assertNotNull(config.proxy);
87+
assertNull(config.proxyAuthenticator);
88+
89+
config = new LDConfig.Builder()
90+
.proxyHost("localhost2")
91+
.proxyPort(4444)
92+
.proxyPassword("proxyPassword")
93+
.build();
94+
assertNotNull(config.proxy);
95+
assertNull(config.proxyAuthenticator);
96+
}
97+
6598
@Test
6699
public void testMinimumPollingIntervalIsEnforcedProperly(){
67100
LDConfig config = new LDConfig.Builder().pollingIntervalMillis(10L).build();

0 commit comments

Comments
 (0)