Skip to content

Commit

Permalink
Start to support sni for http
Browse files Browse the repository at this point in the history
  • Loading branch information
vietj committed Apr 24, 2017
1 parent 012ed75 commit de3608a
Show file tree
Hide file tree
Showing 20 changed files with 281 additions and 58 deletions.
4 changes: 4 additions & 0 deletions src/main/asciidoc/dataobjects.adoc
Expand Up @@ -1841,6 +1841,10 @@ Set the host name to be used by the client request.
+++ +++
Set the port to be used by the client request. Set the port to be used by the client request.
+++ +++
|[[serverName]]`serverName`|`String`|
+++
Set the SNI server name to be used by the client connection.
+++
|[[ssl]]`ssl`|`Boolean`| |[[ssl]]`ssl`|`Boolean`|
+++ +++
Set whether SSL/TLS is enabled Set whether SSL/TLS is enabled
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/io/vertx/core/http/HttpConnection.java
Expand Up @@ -18,13 +18,17 @@


import io.vertx.codegen.annotations.CacheReturn; import io.vertx.codegen.annotations.CacheReturn;
import io.vertx.codegen.annotations.Fluent; import io.vertx.codegen.annotations.Fluent;
import io.vertx.codegen.annotations.GenIgnore;
import io.vertx.codegen.annotations.Nullable; import io.vertx.codegen.annotations.Nullable;
import io.vertx.codegen.annotations.VertxGen; import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.AsyncResult; import io.vertx.core.AsyncResult;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer; import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.SocketAddress; import io.vertx.core.net.SocketAddress;


import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;

/** /**
* Represents an HTTP connection. * Represents an HTTP connection.
* <p/> * <p/>
Expand Down Expand Up @@ -248,4 +252,16 @@ default HttpConnection goAway(long errorCode, int lastStreamId) {
@CacheReturn @CacheReturn
SocketAddress localAddress(); SocketAddress localAddress();


/**
* @return true if this {@link io.vertx.core.http.HttpConnection} is encrypted via SSL/TLS.
*/
boolean isSsl();

/**
* @return an array of the peer certificates. Returns null if connection is
* not SSL.
* @throws javax.net.ssl.SSLPeerUnverifiedException SSL peer's identity has not been verified.
*/
@GenIgnore
X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException;
} }
26 changes: 26 additions & 0 deletions src/main/java/io/vertx/core/http/RequestOptions.java
Expand Up @@ -46,6 +46,12 @@ public class RequestOptions {
*/ */
public static final String DEFAULT_URI = ""; public static final String DEFAULT_URI = "";


/**
* SNI default serve name = null
*/
public static final String DEFAULT_SERVER_NAME = null;

private String serverName;
private String host; private String host;
private int port; private int port;
private boolean ssl; private boolean ssl;
Expand All @@ -55,6 +61,7 @@ public class RequestOptions {
* Default constructor * Default constructor
*/ */
public RequestOptions() { public RequestOptions() {
serverName = DEFAULT_SERVER_NAME;
host = DEFAULT_HOST; host = DEFAULT_HOST;
port = DEFAULT_PORT; port = DEFAULT_PORT;
ssl = DEFAULT_SSL; ssl = DEFAULT_SSL;
Expand Down Expand Up @@ -86,6 +93,25 @@ public RequestOptions(JsonObject json) {
setURI(json.getString("uri", DEFAULT_URI)); setURI(json.getString("uri", DEFAULT_URI));
} }


/**
* Get the SNI server name to be used by the client connection.
*
* @return the SNI server name
*/
public String getServerName() {
return serverName;
}

/**
* Set the SNI server name to be used by the client connection.
*
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setServerName(String serverName) {
this.serverName = serverName;
return this;
}

/** /**
* Get the host name to be used by the client request. * Get the host name to be used by the client request.
* *
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/io/vertx/core/http/impl/ClientConnection.java
Expand Up @@ -21,6 +21,7 @@
import io.netty.handler.codec.http.*; import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.websocketx.*; import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.vertx.codegen.annotations.Nullable; import io.vertx.codegen.annotations.Nullable;
import io.vertx.core.AsyncResult; import io.vertx.core.AsyncResult;
Expand All @@ -41,6 +42,8 @@
import io.vertx.core.net.impl.VertxNetHandler; import io.vertx.core.net.impl.VertxNetHandler;
import io.vertx.core.spi.metrics.HttpClientMetrics; import io.vertx.core.spi.metrics.HttpClientMetrics;


import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;
import java.net.URI; import java.net.URI;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
Expand Down Expand Up @@ -709,4 +712,15 @@ public HttpConnection pingHandler(@Nullable Handler<Buffer> handler) {
public ClientConnection exceptionHandler(Handler<Throwable> handler) { public ClientConnection exceptionHandler(Handler<Throwable> handler) {
return (ClientConnection) super.exceptionHandler(handler); return (ClientConnection) super.exceptionHandler(handler);
} }

@Override
public boolean isSsl() {
return channel.pipeline().get(SslHandler.class) != null;
}


@Override
public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException {
return getPeerCertificateChain();
}
} }
22 changes: 14 additions & 8 deletions src/main/java/io/vertx/core/http/impl/ConnectionManager.java
Expand Up @@ -55,6 +55,7 @@
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;


Expand Down Expand Up @@ -100,11 +101,13 @@ HttpClientMetrics metrics() {


static final class ConnectionKey { static final class ConnectionKey {


private final String serverName;
private final boolean ssl; private final boolean ssl;
private final int port; private final int port;
private final String host; private final String host;


public ConnectionKey(boolean ssl, int port, String host) { public ConnectionKey(String serverName, boolean ssl, int port, String host) {
this.serverName = serverName;
this.ssl = ssl; this.ssl = ssl;
this.host = host; this.host = host;
this.port = port; this.port = port;
Expand All @@ -117,16 +120,18 @@ public boolean equals(Object o) {


ConnectionKey that = (ConnectionKey) o; ConnectionKey that = (ConnectionKey) o;


if (!Objects.equals(serverName, that.serverName)) return false;
if (ssl != that.ssl) return false; if (ssl != that.ssl) return false;
if (port != that.port) return false; if (port != that.port) return false;
if (host != null ? !host.equals(that.host) : that.host != null) return false; if (!Objects.equals(host, that.host)) return false;


return true; return true;
} }


@Override @Override
public int hashCode() { public int hashCode() {
int result = ssl ? 1 : 0; int result = ssl ? 1 : 0;
result = 31 * result + (serverName != null ? serverName.hashCode() : 0);
result = 31 * result + (host != null ? host.hashCode() : 0); result = 31 * result + (host != null ? host.hashCode() : 0);
result = 31 * result + port; result = 31 * result + port;
return result; return result;
Expand Down Expand Up @@ -160,16 +165,16 @@ public void close() {
} }


public void getConnectionForWebsocket(boolean ssl, int port, String host, Waiter waiter) { public void getConnectionForWebsocket(boolean ssl, int port, String host, Waiter waiter) {
ConnectionKey address = new ConnectionKey(ssl, port, host); ConnectionKey address = new ConnectionKey(null, ssl, port, host);
ConnQueue connQueue = wsQM.getConnQueue(address, HttpVersion.HTTP_1_1); ConnQueue connQueue = wsQM.getConnQueue(address, HttpVersion.HTTP_1_1);
connQueue.getConnection(waiter); connQueue.getConnection(waiter);
} }


public void getConnectionForRequest(boolean ssl, HttpVersion version, int port, String host, Waiter waiter) { public void getConnectionForRequest(String serverName, boolean ssl, HttpVersion version, int port, String host, Waiter waiter) {
if (!keepAlive && pipelining) { if (!keepAlive && pipelining) {
waiter.handleFailure(new IllegalStateException("Cannot have pipelining with no keep alive")); waiter.handleFailure(new IllegalStateException("Cannot have pipelining with no keep alive"));
} else { } else {
ConnectionKey address = new ConnectionKey(ssl, port, host); ConnectionKey address = new ConnectionKey(serverName, ssl, port, host);
ConnQueue connQueue = requestQM.getConnQueue(address, version); ConnQueue connQueue = requestQM.getConnQueue(address, version);
connQueue.getConnection(waiter); connQueue.getConnection(waiter);
} }
Expand Down Expand Up @@ -284,7 +289,7 @@ private void createNewConnection(Waiter waiter) {
Bootstrap bootstrap = new Bootstrap(); Bootstrap bootstrap = new Bootstrap();
bootstrap.group(context.nettyEventLoop()); bootstrap.group(context.nettyEventLoop());
bootstrap.channel(NioSocketChannel.class); bootstrap.channel(NioSocketChannel.class);
connector.connect(this, bootstrap, context, address.ssl, pool.version(), address.host, address.port, waiter); connector.connect(this, bootstrap, context, address.serverName, address.ssl, pool.version(), address.host, address.port, waiter);
} }


/** /**
Expand Down Expand Up @@ -407,6 +412,7 @@ protected void connect(
ConnQueue queue, ConnQueue queue,
Bootstrap bootstrap, Bootstrap bootstrap,
ContextImpl context, ContextImpl context,
String serverName,
boolean ssl, boolean ssl,
HttpVersion version, HttpVersion version,
String host, String host,
Expand All @@ -429,7 +435,7 @@ protected void connect(
ChannelPipeline pipeline = ch.pipeline(); ChannelPipeline pipeline = ch.pipeline();
boolean useAlpn = options.isUseAlpn(); boolean useAlpn = options.isUseAlpn();
if (useAlpn) { if (useAlpn) {
SslHandler sslHandler = new SslHandler(sslHelper.createEngine(client.getVertx(), host, port)); SslHandler sslHandler = new SslHandler(sslHelper.createEngine(client.getVertx(), host, port, serverName));
ch.pipeline().addLast(sslHandler); ch.pipeline().addLast(sslHandler);
ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("http/1.1") { ch.pipeline().addLast(new ApplicationProtocolNegotiationHandler("http/1.1") {
@Override @Override
Expand All @@ -447,7 +453,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
}); });
} else { } else {
if (ssl) { if (ssl) {
pipeline.addLast("ssl", new SslHandler(sslHelper.createEngine(vertx, host, port))); pipeline.addLast("ssl", new SslHandler(sslHelper.createEngine(vertx, host, port, serverName)));
} }
if (version == HttpVersion.HTTP_2) { if (version == HttpVersion.HTTP_2) {
if (options.isHttp2ClearTextUpgrade()) { if (options.isHttp2ClearTextUpgrade()) {
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/io/vertx/core/http/impl/Http2ConnectionBase.java
Expand Up @@ -47,6 +47,8 @@
import io.vertx.core.net.NetSocket; import io.vertx.core.net.NetSocket;
import io.vertx.core.net.impl.ConnectionBase; import io.vertx.core.net.impl.ConnectionBase;


import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
Expand Down Expand Up @@ -132,10 +134,15 @@ protected void handleInterestedOpsChanged() {
// Handled by HTTP/2 // Handled by HTTP/2
} }


boolean isSsl() { public boolean isSsl() {
return channel.pipeline().get(SslHandler.class) != null; return channel.pipeline().get(SslHandler.class) != null;
} }


@Override
public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException {
return getPeerCertificateChain();
}

synchronized boolean isClosed() { synchronized boolean isClosed() {
return closed; return closed;
} }
Expand Down Expand Up @@ -459,7 +466,6 @@ public synchronized Http2ConnectionBase exceptionHandler(Handler<Throwable> hand
return (Http2ConnectionBase) super.exceptionHandler(handler); return (Http2ConnectionBase) super.exceptionHandler(handler);
} }



/** /**
* @return the Netty channel - for internal usage only * @return the Netty channel - for internal usage only
*/ */
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/io/vertx/core/http/impl/HttpClientImpl.java
Expand Up @@ -93,7 +93,7 @@ public class HttpClientImpl implements HttpClient, MetricsProvider {
if (uri.getQuery() != null) { if (uri.getQuery() != null) {
requestURI += "?" + uri.getQuery(); requestURI += "?" + uri.getQuery();
} }
return Future.succeededFuture(createRequest(m, uri.getHost(), port, ssl, requestURI, null)); return Future.succeededFuture(createRequest(null, m, uri.getHost(), port, ssl, requestURI, null));
} }
return null; return null;
} catch (Exception e) { } catch (Exception e) {
Expand Down Expand Up @@ -454,12 +454,12 @@ public HttpClientRequest requestAbs(HttpMethod method, String absoluteURI) {
port = 443; port = 443;
} }
} }
return createRequest(method, url.getHost(), port, ssl, url.getFile(), null); return createRequest(null, method, url.getHost(), port, ssl, url.getFile(), null);
} }


@Override @Override
public HttpClientRequest request(HttpMethod method, int port, String host, String requestURI) { public HttpClientRequest request(HttpMethod method, int port, String host, String requestURI) {
return createRequest(method, host, port, null, requestURI, null); return createRequest(null, method, host, port, null, requestURI, null);
} }


@Override @Override
Expand All @@ -469,7 +469,7 @@ public HttpClientRequest request(HttpMethod method, RequestOptions options, Hand


@Override @Override
public HttpClientRequest request(HttpMethod method, RequestOptions options) { public HttpClientRequest request(HttpMethod method, RequestOptions options) {
return createRequest(method, options.getHost(), options.getPort(), options.isSsl(), options.getURI(), null); return createRequest(options.getServerName(), method, options.getHost(), options.getPort(), options.isSsl(), options.getURI(), null);
} }


@Override @Override
Expand Down Expand Up @@ -904,8 +904,8 @@ boolean isCancelled() {
}); });
} }


void getConnectionForRequest(boolean ssl, int port, String host, Waiter waiter) { void getConnectionForRequest(String serverName, boolean ssl, int port, String host, Waiter waiter) {
connectionManager.getConnectionForRequest(ssl, options.getProtocolVersion(), port, host, waiter); connectionManager.getConnectionForRequest(serverName, ssl, options.getProtocolVersion(), port, host, waiter);
} }


/** /**
Expand Down Expand Up @@ -933,11 +933,11 @@ private URL parseUrl(String surl) {
} }


private HttpClient requestNow(HttpMethod method, RequestOptions options, Handler<HttpClientResponse> responseHandler) { private HttpClient requestNow(HttpMethod method, RequestOptions options, Handler<HttpClientResponse> responseHandler) {
createRequest(method, options.getHost(), options.getPort(), options.isSsl(), options.getURI(), null).handler(responseHandler).end(); createRequest(null, method, options.getHost(), options.getPort(), options.isSsl(), options.getURI(), null).handler(responseHandler).end();
return this; return this;
} }


private HttpClientRequest createRequest(HttpMethod method, String host, int port, Boolean ssl, String relativeURI, MultiMap headers) { private HttpClientRequest createRequest(String serverName, HttpMethod method, String host, int port, Boolean ssl, String relativeURI, MultiMap headers) {
Objects.requireNonNull(method, "no null method accepted"); Objects.requireNonNull(method, "no null method accepted");
Objects.requireNonNull(host, "no null host accepted"); Objects.requireNonNull(host, "no null host accepted");
Objects.requireNonNull(relativeURI, "no null relativeURI accepted"); Objects.requireNonNull(relativeURI, "no null relativeURI accepted");
Expand All @@ -955,11 +955,11 @@ private HttpClientRequest createRequest(HttpMethod method, String host, int port
headers.add("Proxy-Authorization", "Basic " + Base64.getEncoder() headers.add("Proxy-Authorization", "Basic " + Base64.getEncoder()
.encodeToString((proxyOptions.getUsername() + ":" + proxyOptions.getPassword()).getBytes())); .encodeToString((proxyOptions.getUsername() + ":" + proxyOptions.getPassword()).getBytes()));
} }
req = new HttpClientRequestImpl(this, useSSL, method, proxyOptions.getHost(), proxyOptions.getPort(), req = new HttpClientRequestImpl(this, useSSL, serverName, method, proxyOptions.getHost(), proxyOptions.getPort(),
relativeURI, vertx); relativeURI, vertx);
req.setHost(host + (port != 80 ? ":" + port : "")); req.setHost(host + (port != 80 ? ":" + port : ""));
} else { } else {
req = new HttpClientRequestImpl(this, useSSL, method, host, port, relativeURI, vertx); req = new HttpClientRequestImpl(this, useSSL, serverName, method, host, port, relativeURI, vertx);
} }
if (headers != null) { if (headers != null) {
req.headers().setAll(headers); req.headers().setAll(headers);
Expand Down
Expand Up @@ -51,7 +51,7 @@
public class HttpClientRequestImpl extends HttpClientRequestBase implements HttpClientRequest { public class HttpClientRequestImpl extends HttpClientRequestBase implements HttpClientRequest {


private final VertxInternal vertx; private final VertxInternal vertx;
private final int port; private final String serverName;
private Handler<HttpClientResponse> respHandler; private Handler<HttpClientResponse> respHandler;
private Handler<Void> endHandler; private Handler<Void> endHandler;
private boolean chunked; private boolean chunked;
Expand All @@ -77,12 +77,12 @@ public class HttpClientRequestImpl extends HttpClientRequestBase implements Http
private long written; private long written;
private CaseInsensitiveHeaders headers; private CaseInsensitiveHeaders headers;


HttpClientRequestImpl(HttpClientImpl client, boolean ssl, HttpMethod method, String host, int port, HttpClientRequestImpl(HttpClientImpl client, boolean ssl, String serverName, HttpMethod method, String host, int port,
String relativeURI, VertxInternal vertx) { String relativeURI, VertxInternal vertx) {
super(client, ssl, method, host, port, relativeURI); super(client, ssl, method, host, port, relativeURI);
this.chunked = false; this.chunked = false;
this.vertx = vertx; this.vertx = vertx;
this.port = port; this.serverName = serverName;
} }


@Override @Override
Expand Down Expand Up @@ -732,7 +732,7 @@ boolean isCancelled() {
// We defer actual connection until the first part of body is written or end is called // We defer actual connection until the first part of body is written or end is called
// This gives the user an opportunity to set an exception handler before connecting so // This gives the user an opportunity to set an exception handler before connecting so
// they can capture any exceptions on connection // they can capture any exceptions on connection
client.getConnectionForRequest(ssl, port, host, waiter); client.getConnectionForRequest(serverName, ssl, port, host, waiter);
connecting = true; connecting = true;
} }
} }
Expand Down

0 comments on commit de3608a

Please sign in to comment.