Skip to content

Commit

Permalink
- ssl and plain socket factories can now be configured in HttpClientC…
Browse files Browse the repository at this point in the history
…onfig

- added authentication and https section to readme
- resolves searchbox-io#179
  • Loading branch information
kramer committed Apr 21, 2015
1 parent 51947d6 commit 3707ee1
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProviderHC4;

import java.util.Collection;
Expand All @@ -20,13 +24,17 @@ public class DroidClientConfig extends ClientConfig {
private final Integer defaultMaxTotalConnectionPerRoute;
private final Map<HttpRoute, Integer> maxTotalConnectionPerRoute;
private final CredentialsProvider credentialsProvider;
private final LayeredConnectionSocketFactory sslSocketFactory;
private final ConnectionSocketFactory plainSocketFactory;

public DroidClientConfig(Builder builder) {
super(builder);
this.maxTotalConnection = builder.maxTotalConnection;
this.defaultMaxTotalConnectionPerRoute = builder.defaultMaxTotalConnectionPerRoute;
this.maxTotalConnectionPerRoute = builder.maxTotalConnectionPerRoute;
this.credentialsProvider = builder.credentialsProvider;
this.plainSocketFactory = builder.plainSocketFactory;
this.sslSocketFactory = builder.sslSocketFactory;
}

public Map<HttpRoute, Integer> getMaxTotalConnectionPerRoute() {
Expand All @@ -45,12 +53,22 @@ public CredentialsProvider getCredentialsProvider() {
return credentialsProvider;
}

public LayeredConnectionSocketFactory getSslSocketFactory() {
return sslSocketFactory;
}

public ConnectionSocketFactory getPlainSocketFactory() {
return plainSocketFactory;
}

public static class Builder extends ClientConfig.AbstractBuilder<DroidClientConfig, Builder> {

private Integer maxTotalConnection;
private Integer defaultMaxTotalConnectionPerRoute;
private Map<HttpRoute, Integer> maxTotalConnectionPerRoute = new HashMap<HttpRoute, Integer>();
private CredentialsProvider credentialsProvider;
private LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
private ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();

public Builder(DroidClientConfig httpClientConfig) {
super(httpClientConfig);
Expand Down Expand Up @@ -105,6 +123,24 @@ public Builder defaultCredentials(String username, String password) {
return this;
}

/**
*
* @param socketFactory The socket factory instance that will be registered for <code>https</code> scheme.
*/
public Builder sslSocketFactory(LayeredConnectionSocketFactory socketFactory) {
this.sslSocketFactory = socketFactory;
return this;
}

/**
*
* @param socketFactory The socket factory instance that will be registered for <code>http</code> scheme.
*/
public Builder plainSocketFactory(ConnectionSocketFactory socketFactory) {
this.plainSocketFactory = socketFactory;
return this;
}

public DroidClientConfig build() {
return new DroidClientConfig(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
import io.searchbox.client.config.discovery.NodeChecker;
import io.searchbox.client.config.idle.IdleConnectionReaper;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -25,13 +29,19 @@ public class JestClientFactory {
public JestClient getObject() {
JestDroidClient client = new JestDroidClient();


Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", droidClientConfig.getPlainSocketFactory())
.register("https", droidClientConfig.getSslSocketFactory())
.build();

if (droidClientConfig != null) {
log.debug("Creating HTTP client based on configuration");
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
client.setServers(droidClientConfig.getServerList());
boolean isMultiThreaded = droidClientConfig.isMultiThreaded();
if (isMultiThreaded) {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);

Integer maxTotal = droidClientConfig.getMaxTotalConnection();
if (maxTotal != null) {
Expand All @@ -44,8 +54,8 @@ public JestClient getObject() {
}

Map<HttpRoute, Integer> maxPerRoute = droidClientConfig.getMaxTotalConnectionPerRoute();
for (HttpRoute route : maxPerRoute.keySet()) {
cm.setMaxPerRoute(route, maxPerRoute.get(route));
for (Map.Entry<HttpRoute, Integer> entry : maxPerRoute.entrySet()) {
cm.setMaxPerRoute(entry.getKey(), entry.getValue());
}
httpClientBuilder.setConnectionManager(cm);
log.debug("Multi Threaded http client created");
Expand All @@ -62,6 +72,7 @@ public JestClient getObject() {

} else {
log.debug("Default http client is created without multi threaded option");
httpClientBuilder.setConnectionManager(new BasicHttpClientConnectionManager(registry));
}

httpClientBuilder.setDefaultRequestConfig(
Expand Down
60 changes: 60 additions & 0 deletions jest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,66 @@ ClientConfig clientConfig = new ClientConfig.Builder("http://localhost:9200")
.build();
```

### Authentication

Basic username and password authentication can be configured when constructing the client; it should be noted that
these credentials will be used for all servers provided and discovered.

```java
JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(
new HttpClientConfig.Builder("http://localhost:9200")
.defaultCredentials("global_user", "global_password")
.build()
);
```

If your authentication needs are more complicated than above (e.g.: different credentials for different servers, Kerberos etc.)
then you can also provide a `CredentialsProvider` instance.

```java
BasicCredentialsProvider customCredentialsProvider = new BasicCredentialsProvider();
customCredentialsProvider.setCredentials(
new AuthScope("192.168.0.88", 9200),
new UsernamePasswordCredentials("eu_user", "123")
);
customCredentialsProvider.setCredentials(
new AuthScope("192.168.0.172", 9200),
new UsernamePasswordCredentials("us_user", "456")
);

JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(
new HttpClientConfig.Builder(Arrays.asList("http://192.168.0.88:9200", "http://192.168.0.172:9200"))
.credentialsProvider(customCredentialsProvider)
.build()
);
```

### HTTPS / SSL

HTTPS or SSL (or TLS) connections can be configured by passing your own instance of `LayeredConnectionSocketFactory` to the builder.

```java
// trust ALL certificates
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
return true;
}
}).build();

// skip hostname checks
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);

JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(new HttpClientConfig.Builder("https://localhost:9200")
.sslSocketFactory(sslSocketFactory)
.build()
);
```
Keep in mind that (the `SSLContext` and `HostnameVerifier` in) above example is just for example and very insecure as it is.

### Further Reading

Expand Down
30 changes: 22 additions & 8 deletions jest/src/main/java/io/searchbox/client/JestClientFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
import io.searchbox.client.http.JestHttpClient;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
Expand Down Expand Up @@ -141,24 +144,35 @@ protected RequestConfig getRequestConfig() {

// Extension point
protected HttpClientConnectionManager getConnectionManager() {
HttpClientConnectionManager retval;

Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", httpClientConfig.getPlainSocketFactory())
.register("https", httpClientConfig.getSslSocketFactory())
.build();

if (httpClientConfig.isMultiThreaded()) {
log.info("Using multi thread/connection supporting pooling connection manager");
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
final PoolingHttpClientConnectionManager poolingConnMgr = new PoolingHttpClientConnectionManager(registry);

final Integer maxTotal = httpClientConfig.getMaxTotalConnection();
if (maxTotal != null) {
cm.setMaxTotal(maxTotal);
poolingConnMgr.setMaxTotal(maxTotal);
}
final Integer defaultMaxPerRoute = httpClientConfig.getDefaultMaxTotalConnectionPerRoute();
if (defaultMaxPerRoute != null) {
cm.setDefaultMaxPerRoute(defaultMaxPerRoute);
poolingConnMgr.setDefaultMaxPerRoute(defaultMaxPerRoute);
}
final Map<HttpRoute, Integer> maxPerRoute = httpClientConfig.getMaxTotalConnectionPerRoute();
for (HttpRoute route : maxPerRoute.keySet()) {
cm.setMaxPerRoute(route, maxPerRoute.get(route));
for (Map.Entry<HttpRoute, Integer> entry : maxPerRoute.entrySet()) {
poolingConnMgr.setMaxPerRoute(entry.getKey(), entry.getValue());
}
return cm;
retval = poolingConnMgr;
} else {
log.info("Using single thread/connection supporting basic connection manager");
retval = new BasicHttpClientConnectionManager(registry);
}
log.info("Using single thread/connection supporting basic connection manager");
return new BasicHttpClientConnectionManager();

return retval;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;

import java.util.Collection;
Expand All @@ -20,13 +24,17 @@ public class HttpClientConfig extends ClientConfig {
private final Integer defaultMaxTotalConnectionPerRoute;
private final Map<HttpRoute, Integer> maxTotalConnectionPerRoute;
private final CredentialsProvider credentialsProvider;
private final LayeredConnectionSocketFactory sslSocketFactory;
private final ConnectionSocketFactory plainSocketFactory;

public HttpClientConfig(Builder builder) {
super(builder);
this.maxTotalConnection = builder.maxTotalConnection;
this.defaultMaxTotalConnectionPerRoute = builder.defaultMaxTotalConnectionPerRoute;
this.maxTotalConnectionPerRoute = builder.maxTotalConnectionPerRoute;
this.credentialsProvider = builder.credentialsProvider;
this.sslSocketFactory = builder.sslSocketFactory;
this.plainSocketFactory = builder.plainSocketFactory;
}

public Map<HttpRoute, Integer> getMaxTotalConnectionPerRoute() {
Expand All @@ -45,12 +53,22 @@ public CredentialsProvider getCredentialsProvider() {
return credentialsProvider;
}

public LayeredConnectionSocketFactory getSslSocketFactory() {
return sslSocketFactory;
}

public ConnectionSocketFactory getPlainSocketFactory() {
return plainSocketFactory;
}

public static class Builder extends ClientConfig.AbstractBuilder<HttpClientConfig, Builder> {

private Integer maxTotalConnection;
private Integer defaultMaxTotalConnectionPerRoute;
private Map<HttpRoute, Integer> maxTotalConnectionPerRoute = new HashMap<HttpRoute, Integer>();
private CredentialsProvider credentialsProvider;
private LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
private ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();

public Builder(HttpClientConfig httpClientConfig) {
super(httpClientConfig);
Expand Down Expand Up @@ -105,6 +123,24 @@ public Builder defaultCredentials(String username, String password) {
return this;
}

/**
*
* @param socketFactory The socket factory instance that will be registered for <code>https</code> scheme.
*/
public Builder sslSocketFactory(LayeredConnectionSocketFactory socketFactory) {
this.sslSocketFactory = socketFactory;
return this;
}

/**
*
* @param socketFactory The socket factory instance that will be registered for <code>http</code> scheme.
*/
public Builder plainSocketFactory(ConnectionSocketFactory socketFactory) {
this.plainSocketFactory = socketFactory;
return this;
}

public HttpClientConfig build() {
return new HttpClientConfig(this);
}
Expand Down

0 comments on commit 3707ee1

Please sign in to comment.