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.
+++
|[[serverName]]`serverName`|`String`|
+++
Set the SNI server name to be used by the client connection.
+++
|[[ssl]]`ssl`|`Boolean`|
+++
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.Fluent;
import io.vertx.codegen.annotations.GenIgnore;
import io.vertx.codegen.annotations.Nullable;
import io.vertx.codegen.annotations.VertxGen;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.SocketAddress;

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

/**
* Represents an HTTP connection.
* <p/>
Expand Down Expand Up @@ -248,4 +252,16 @@ default HttpConnection goAway(long errorCode, int lastStreamId) {
@CacheReturn
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 = "";

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

private String serverName;
private String host;
private int port;
private boolean ssl;
Expand All @@ -55,6 +61,7 @@ public class RequestOptions {
* Default constructor
*/
public RequestOptions() {
serverName = DEFAULT_SERVER_NAME;
host = DEFAULT_HOST;
port = DEFAULT_PORT;
ssl = DEFAULT_SSL;
Expand Down Expand Up @@ -86,6 +93,25 @@ public RequestOptions(JsonObject json) {
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.
*
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.HttpHeaders;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.ReferenceCountUtil;
import io.vertx.codegen.annotations.Nullable;
import io.vertx.core.AsyncResult;
Expand All @@ -41,6 +42,8 @@
import io.vertx.core.net.impl.VertxNetHandler;
import io.vertx.core.spi.metrics.HttpClientMetrics;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;
import java.net.URI;
import java.util.ArrayDeque;
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) {
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 java.util.ArrayDeque;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;

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

static final class ConnectionKey {

private final String serverName;
private final boolean ssl;
private final int port;
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.host = host;
this.port = port;
Expand All @@ -117,16 +120,18 @@ public boolean equals(Object o) {

ConnectionKey that = (ConnectionKey) o;

if (!Objects.equals(serverName, that.serverName)) return false;
if (ssl != that.ssl) 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;
}

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

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.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) {
waiter.handleFailure(new IllegalStateException("Cannot have pipelining with no keep alive"));
} else {
ConnectionKey address = new ConnectionKey(ssl, port, host);
ConnectionKey address = new ConnectionKey(serverName, ssl, port, host);
ConnQueue connQueue = requestQM.getConnQueue(address, version);
connQueue.getConnection(waiter);
}
Expand Down Expand Up @@ -284,7 +289,7 @@ private void createNewConnection(Waiter waiter) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(context.nettyEventLoop());
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,
Bootstrap bootstrap,
ContextImpl context,
String serverName,
boolean ssl,
HttpVersion version,
String host,
Expand All @@ -429,7 +435,7 @@ protected void connect(
ChannelPipeline pipeline = ch.pipeline();
boolean useAlpn = options.isUseAlpn();
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(new ApplicationProtocolNegotiationHandler("http/1.1") {
@Override
Expand All @@ -447,7 +453,7 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
});
} else {
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 (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.impl.ConnectionBase;

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

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

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

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


/**
* @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) {
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;
} catch (Exception e) {
Expand Down Expand Up @@ -454,12 +454,12 @@ public HttpClientRequest requestAbs(HttpMethod method, String absoluteURI) {
port = 443;
}
}
return createRequest(method, url.getHost(), port, ssl, url.getFile(), null);
return createRequest(null, method, url.getHost(), port, ssl, url.getFile(), null);
}

@Override
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
Expand All @@ -469,7 +469,7 @@ public HttpClientRequest request(HttpMethod method, RequestOptions options, Hand

@Override
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
Expand Down Expand Up @@ -904,8 +904,8 @@ boolean isCancelled() {
});
}

void getConnectionForRequest(boolean ssl, int port, String host, Waiter waiter) {
connectionManager.getConnectionForRequest(ssl, options.getProtocolVersion(), port, host, waiter);
void getConnectionForRequest(String serverName, boolean ssl, int port, String host, Waiter 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) {
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;
}

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(host, "no null host 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()
.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);
req.setHost(host + (port != 80 ? ":" + port : ""));
} 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) {
req.headers().setAll(headers);
Expand Down
Expand Up @@ -51,7 +51,7 @@
public class HttpClientRequestImpl extends HttpClientRequestBase implements HttpClientRequest {

private final VertxInternal vertx;
private final int port;
private final String serverName;
private Handler<HttpClientResponse> respHandler;
private Handler<Void> endHandler;
private boolean chunked;
Expand All @@ -77,12 +77,12 @@ public class HttpClientRequestImpl extends HttpClientRequestBase implements Http
private long written;
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) {
super(client, ssl, method, host, port, relativeURI);
this.chunked = false;
this.vertx = vertx;
this.port = port;
this.serverName = serverName;
}

@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
// This gives the user an opportunity to set an exception handler before connecting so
// they can capture any exceptions on connection
client.getConnectionForRequest(ssl, port, host, waiter);
client.getConnectionForRequest(serverName, ssl, port, host, waiter);
connecting = true;
}
}
Expand Down

0 comments on commit de3608a

Please sign in to comment.