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

Commit

Permalink
Merge pull request #4 from launchdarkly/dr/proxyAuth
Browse files Browse the repository at this point in the history
Add proxy authentication support
  • Loading branch information
drichelson authored Apr 11, 2017
2 parents 08c0895 + a8ea8e3 commit 18b86cf
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 10 deletions.
23 changes: 22 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
Contributing to the LaunchDarkly SDK for Java
================================================

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.
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.


Testing Proxy Settings
==================
Installation is your own journey, but your squid.conf file should have auth/access sections that look something like this:

```
auth_param basic program /usr/local/Cellar/squid/3.5.6/libexec/basic_ncsa_auth <SQUID_DIR>/passwords
auth_param basic realm proxy
acl authenticated proxy_auth REQUIRED
http_access allow authenticated
# And finally deny all other access to this proxy
http_access deny all
```

The contents of the passwords file is:
```
user:$apr1$sBfNiLFJ$7h3S84EgJhlbWM3v.90v61
```

The username/password is: user/password
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dependencies {
compile "com.google.guava:guava:19.0"
compile "joda-time:joda-time:2.9.3"
compile "org.slf4j:slf4j-api:1.7.21"
compile group: "com.launchdarkly", name: "okhttp-eventsource", version: "1.2.0-SNAPSHOT", changing: true
compile group: "com.launchdarkly", name: "okhttp-eventsource", version: "1.3.0", changing: true
compile "redis.clients:jedis:2.9.0"
testCompile "org.easymock:easymock:3.4"
testCompile 'junit:junit:4.12'
Expand Down
72 changes: 64 additions & 8 deletions src/main/java/com/launchdarkly/client/LDConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import com.google.common.io.Files;
import com.google.gson.Gson;
import okhttp3.Authenticator;
import okhttp3.Cache;
import okhttp3.ConnectionPool;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
Expand Down Expand Up @@ -46,6 +51,7 @@ public final class LDConfig {
final int socketTimeoutMillis;
final int flushInterval;
final Proxy proxy;
final Authenticator proxyAuthenticator;
final OkHttpClient httpClient;
final boolean stream;
final FeatureStore featureStore;
Expand All @@ -64,6 +70,7 @@ protected LDConfig(Builder builder) {
this.socketTimeoutMillis = builder.socketTimeoutMillis;
this.flushInterval = builder.flushIntervalSeconds;
this.proxy = builder.proxy();
this.proxyAuthenticator = builder.proxyAuthenticator();
this.streamURI = builder.streamURI;
this.stream = builder.stream;
this.featureStore = builder.featureStore;
Expand Down Expand Up @@ -91,12 +98,24 @@ protected LDConfig(Builder builder) {

if (proxy != null) {
httpClientBuilder.proxy(proxy);
if (proxyAuthenticator != null) {
httpClientBuilder.proxyAuthenticator(proxyAuthenticator);
logger.info("Using proxy: " + proxy + " with authentication.");
} else {
logger.info("Using proxy: " + proxy + " without authentication.");
}
}

httpClient = httpClientBuilder
.build();
}

Request.Builder getRequestBuilder(String sdkKey) {
return new Request.Builder()
.addHeader("Authorization", sdkKey)
.addHeader("User-Agent", "JavaClient/" + LDClient.CLIENT_VERSION);
}

/**
* A <a href="http://en.wikipedia.org/wiki/Builder_pattern">builder</a> that helps construct {@link com.launchdarkly.client.LDConfig} objects. Builder
* calls can be chained, enabling the following pattern:
Expand All @@ -118,6 +137,8 @@ public static class Builder {
private int flushIntervalSeconds = DEFAULT_FLUSH_INTERVAL_SECONDS;
private String proxyHost = "localhost";
private int proxyPort = -1;
private String proxyUsername = null;
private String proxyPassword = null;
private boolean stream = true;
private boolean useLdd = false;
private boolean offline = false;
Expand Down Expand Up @@ -289,6 +310,30 @@ public Builder proxyPort(int port) {
return this;
}

/**
* Sets the username for the optional HTTP proxy. Only used when {@link LDConfig.Builder#proxyPassword(String)}
* is also called.
*
* @param username
* @return the builder
*/
public Builder proxyUsername(String username) {
this.proxyUsername = username;
return this;
}

/**
* Sets the password for the optional HTTP proxy. Only used when {@link LDConfig.Builder#proxyUsername(String)}
* is also called.
*
* @param password
* @return the builder
*/
public Builder proxyPassword(String password) {
this.proxyPassword = password;
return this;
}

/**
* Deprecated. Only HTTP proxies are currently supported.
*
Expand Down Expand Up @@ -352,7 +397,7 @@ public Builder startWaitMillis(long startWaitMillis) {
* Enable event sampling. When set to the default of zero, sampling is disabled and all events
* are sent back to LaunchDarkly. When set to greater than zero, there is a 1 in
* <code>samplingInterval</code> chance events will be will be sent.
*
* <p>
* <p>Example: if you want 5% sampling rate, set <code>samplingInterval</code> to 20.
*
* @param samplingInterval the sampling interval.
Expand Down Expand Up @@ -386,6 +431,24 @@ Proxy proxy() {
}
}

Authenticator proxyAuthenticator() {
if (this.proxyUsername != null && this.proxyPassword != null) {
final String credential = Credentials.basic(proxyUsername, proxyPassword);
return new Authenticator() {
public Request authenticate(Route route, Response response) throws IOException {
if (response.request().header("Proxy-Authorization") != null) {
return null; // Give up, we've already failed to authenticate with the proxy.
} else {
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
}
};
}
return null;
}

/**
* Build the configured {@link com.launchdarkly.client.LDConfig} object
*
Expand All @@ -394,12 +457,5 @@ Proxy proxy() {
public LDConfig build() {
return new LDConfig(this);
}

}

Request.Builder getRequestBuilder(String sdkKey) {
return new Request.Builder()
.addHeader("Authorization", sdkKey)
.addHeader("User-Agent", "JavaClient/" + LDClient.CLIENT_VERSION);
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/launchdarkly/client/StreamProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ public void onError(Throwable throwable) {

if (config.proxy != null) {
builder.proxy(config.proxy);
if (config.proxyAuthenticator != null) {
builder.proxyAuthenticator(config.proxyAuthenticator);
}
}

es = builder.build();
Expand Down
33 changes: 33 additions & 0 deletions src/test/java/com/launchdarkly/client/LDConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.net.Proxy;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

public class LDConfigTest {
Expand Down Expand Up @@ -40,6 +41,7 @@ public void testSocketTimeoutSpecifiedInMilliseconds() {
public void testNoProxyConfigured() {
LDConfig config = new LDConfig.Builder().build();
assertNull(config.proxy);
assertNull(config.proxyAuthenticator);
}

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

@Test
public void testProxyAuth() {
LDConfig config = new LDConfig.Builder()
.proxyHost("localhost2")
.proxyPort(4444)
.proxyUsername("proxyUser")
.proxyPassword("proxyPassword")
.build();
assertNotNull(config.proxy);
assertNotNull(config.proxyAuthenticator);
}

@Test
public void testProxyAuthPartialConfig() {
LDConfig config = new LDConfig.Builder()
.proxyHost("localhost2")
.proxyPort(4444)
.proxyUsername("proxyUser")
.build();
assertNotNull(config.proxy);
assertNull(config.proxyAuthenticator);

config = new LDConfig.Builder()
.proxyHost("localhost2")
.proxyPort(4444)
.proxyPassword("proxyPassword")
.build();
assertNotNull(config.proxy);
assertNull(config.proxyAuthenticator);
}

@Test
public void testMinimumPollingIntervalIsEnforcedProperly(){
LDConfig config = new LDConfig.Builder().pollingIntervalMillis(10L).build();
Expand Down

0 comments on commit 18b86cf

Please sign in to comment.