/* * Copyright (c) 2011-2013 The original author or authors * ------------------------------------------------------ * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.core.http.impl; import io.vertx.core.Closeable; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.MultiMap; import io.vertx.core.VertxException; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpVersion; import io.vertx.core.http.RequestOptions; import io.vertx.core.http.WebSocket; import io.vertx.core.http.WebsocketVersion; import io.vertx.core.impl.ContextImpl; import io.vertx.core.impl.VertxInternal; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyType; import io.vertx.core.net.impl.SSLHelper; import io.vertx.core.spi.metrics.HttpClientMetrics; import io.vertx.core.spi.metrics.Metrics; import io.vertx.core.spi.metrics.MetricsProvider; import io.vertx.core.streams.ReadStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Function; /** * * This class is thread-safe * * @author <a href="http://tfox.org">Tim Fox</a> */ public class HttpClientImpl implements HttpClient, MetricsProvider { private final Function<HttpClientResponse, Future<HttpClientRequest>> DEFAULT_HANDLER = resp -> { try { int statusCode = resp.statusCode(); String location = resp.getHeader(HttpHeaders.LOCATION); if (location != null && (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307)) { HttpMethod m = resp.request().method(); if (statusCode == 301 || statusCode == 302 || statusCode == 303) { m = HttpMethod.GET; } URI uri = HttpUtils.resolveURIReference(resp.request().absoluteURI(), location); boolean ssl; int port = uri.getPort(); String protocol = uri.getScheme(); char chend = protocol.charAt(protocol.length() - 1); if (chend == 'p') { ssl = false; if (port == -1) { port = 80; } } else if (chend == 's') { ssl = true; if (port == -1) { port = 443; } } else { return null; } String requestURI = uri.getPath(); if (uri.getQuery() != null) { requestURI += "?" + uri.getQuery(); } return Future.succeededFuture(createRequest(m, uri.getHost(), port, ssl, requestURI, null)); } return null; } catch (Exception e) { return Future.failedFuture(e); } }; private static final Logger log = LoggerFactory.getLogger(HttpClientImpl.class); private final VertxInternal vertx; private final HttpClientOptions options; private final ContextImpl creatingContext; private final ConnectionManager connectionManager; private final Closeable closeHook; private final ProxyType proxyType; private final SSLHelper sslHelper; private volatile boolean closed; private volatile Function<HttpClientResponse, Future<HttpClientRequest>> redirectHandler = DEFAULT_HANDLER; public HttpClientImpl(VertxInternal vertx, HttpClientOptions options) { if (options.isUseAlpn() && !options.isSsl()) { throw new IllegalArgumentException("Must enable SSL when using ALPN"); } this.vertx = vertx; this.options = new HttpClientOptions(options); List<HttpVersion> alpnVersions = options.getAlpnVersions(); if (alpnVersions == null || alpnVersions.isEmpty()) { switch (options.getProtocolVersion()) { case HTTP_2: alpnVersions = Arrays.asList(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1); break; default: alpnVersions = Collections.singletonList(options.getProtocolVersion()); break; } } this.sslHelper = new SSLHelper(options, options.getKeyCertOptions(), options.getTrustOptions()). setApplicationProtocols(alpnVersions); this.creatingContext = vertx.getContext(); closeHook = completionHandler -> { HttpClientImpl.this.close(); completionHandler.handle(Future.succeededFuture()); }; if (creatingContext != null) { if (creatingContext.isMultiThreadedWorkerContext()) { throw new IllegalStateException("Cannot use HttpClient in a multi-threaded worker verticle"); } if(options.getProtocolVersion() == HttpVersion.HTTP_2 && Context.isOnWorkerThread()) { throw new IllegalStateException("Cannot use HttpClient with HTTP_2 in a worker"); } creatingContext.addCloseHook(closeHook); } HttpClientMetrics metrics = vertx.metricsSPI().createMetrics(this, options); connectionManager = new ConnectionManager(this, metrics); proxyType = options.getProxyOptions() != null ? options.getProxyOptions().getType() : null; } @Override public HttpClient websocket(RequestOptions options, Handler<WebSocket> wsConnect) { return websocket(options.getPort(), options.getHost(), options.getURI(), wsConnect); } @Override public HttpClient websocket(int port, String host, String requestURI, Handler<WebSocket> wsConnect) { websocketStream(port, host, requestURI, null, null).handler(wsConnect); return this; } @Override public HttpClient websocket(RequestOptions options, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getPort(), options.getHost(), options.getURI(), wsConnect, failureHandler); } public HttpClient websocket(int port, String host, String requestURI, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler){ websocketStream(port, host, requestURI, null, null).exceptionHandler(failureHandler).handler(wsConnect); return this; } @Override public HttpClient websocket(String host, String requestURI, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), host, requestURI, wsConnect); } @Override public HttpClient websocket(String host, String requestURI, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), host, requestURI, wsConnect, failureHandler); } @Override public HttpClient websocket(RequestOptions options, MultiMap headers, Handler<WebSocket> wsConnect) { return websocket(options.getPort(), options.getHost(), options.getURI(), headers, wsConnect); } @Override public HttpClient websocket(int port, String host, String requestURI, MultiMap headers, Handler<WebSocket> wsConnect) { websocketStream(port, host, requestURI, headers, null).handler(wsConnect); return this; } @Override public HttpClient websocket(RequestOptions options, MultiMap headers, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getPort(), options.getHost(), options.getURI(), headers, wsConnect, failureHandler); } @Override public HttpClient websocket(int port, String host, String requestURI, MultiMap headers, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { websocketStream(port, host, requestURI, headers, null).exceptionHandler(failureHandler).handler(wsConnect); return this; } @Override public HttpClient websocket(String host, String requestURI, MultiMap headers, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), host, requestURI, headers, wsConnect); } @Override public HttpClient websocket(String host, String requestURI, MultiMap headers, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), host, requestURI, headers, wsConnect, failureHandler); } @Override public HttpClient websocket(RequestOptions options, MultiMap headers, WebsocketVersion version, Handler<WebSocket> wsConnect) { return websocket(options.getPort(), options.getHost(), options.getURI(), headers, version, wsConnect); } @Override public HttpClient websocket(int port, String host, String requestURI, MultiMap headers, WebsocketVersion version, Handler<WebSocket> wsConnect) { websocketStream(port, host, requestURI, headers, version, null).handler(wsConnect); return this; } @Override public HttpClient websocket(RequestOptions options, MultiMap headers, WebsocketVersion version, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getPort(), options.getHost(), options.getURI(), headers, version, wsConnect, failureHandler); } @Override public HttpClient websocket(int port, String host, String requestURI, MultiMap headers, WebsocketVersion version , Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { websocketStream(port, host, requestURI, headers, version, null).exceptionHandler(failureHandler).handler(wsConnect); return this; } @Override public HttpClient websocket(String host, String requestURI, MultiMap headers, WebsocketVersion version, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), host, requestURI, headers, version, wsConnect); } @Override public HttpClient websocket(String host, String requestURI, MultiMap headers, WebsocketVersion version , Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), host, requestURI, headers, version, wsConnect, failureHandler); } @Override public HttpClient websocket(RequestOptions options, MultiMap headers, WebsocketVersion version, String subProtocols, Handler<WebSocket> wsConnect) { return websocket(options.getPort(), options.getHost(), options.getURI(), headers, version, subProtocols, wsConnect); } @Override public HttpClient websocket(int port, String host, String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols, Handler<WebSocket> wsConnect) { websocketStream(port, host, requestURI, headers, version, subProtocols).handler(wsConnect); return this; } @Override public HttpClient websocket(RequestOptions options, MultiMap headers, WebsocketVersion version, String subProtocols, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getPort(), options.getHost(), options.getURI(), headers, version, subProtocols, wsConnect, failureHandler); } @Override public HttpClient websocket(int port, String host, String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { websocketStream(port, host, requestURI, headers, version, subProtocols).exceptionHandler(failureHandler).handler(wsConnect); return this; } @Override public HttpClient websocket(String host, String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), host, requestURI, headers, version, subProtocols, wsConnect); } @Override public HttpClient websocket(String host, String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols , Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), host, requestURI, headers, version, subProtocols, wsConnect, failureHandler); } @Override public HttpClient websocket(String requestURI, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, wsConnect); } @Override public HttpClient websocket(String requestURI, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, wsConnect, failureHandler); } @Override public HttpClient websocket(String requestURI, MultiMap headers, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, wsConnect); } @Override public HttpClient websocket(String requestURI, MultiMap headers, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, wsConnect, failureHandler); } @Override public HttpClient websocket(String requestURI, MultiMap headers, WebsocketVersion version, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, version, wsConnect); } @Override public HttpClient websocket(String requestURI, MultiMap headers, WebsocketVersion version, Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, version, wsConnect, failureHandler); } @Override public HttpClient websocket(String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols, Handler<WebSocket> wsConnect) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, version, subProtocols, wsConnect); } @Override public HttpClient websocket(String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols , Handler<WebSocket> wsConnect, Handler<Throwable> failureHandler) { return websocket(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, version, subProtocols , wsConnect, failureHandler); } @Override public WebSocketStream websocketStream(RequestOptions options) { return websocketStream(options, null); } @Override public WebSocketStream websocketStream(int port, String host, String requestURI) { return websocketStream(port, host, requestURI, null, null); } @Override public WebSocketStream websocketStream(String host, String requestURI) { return websocketStream(options.getDefaultPort(), host, requestURI); } @Override public WebSocketStream websocketStream(RequestOptions options, MultiMap headers) { return websocketStream(options, headers, null); } @Override public WebSocketStream websocketStream(int port, String host, String requestURI, MultiMap headers) { return websocketStream(port, host, requestURI, headers, null); } @Override public WebSocketStream websocketStream(String host, String requestURI, MultiMap headers) { return websocketStream(options.getDefaultPort(), host, requestURI, headers); } @Override public WebSocketStream websocketStream(RequestOptions options, MultiMap headers, WebsocketVersion version) { return websocketStream(options, headers, version, null); } @Override public WebSocketStream websocketStream(int port, String host, String requestURI, MultiMap headers, WebsocketVersion version) { return websocketStream(port, host, requestURI, headers, version, null); } @Override public WebSocketStream websocketStream(String host, String requestURI, MultiMap headers, WebsocketVersion version) { return websocketStream(options.getDefaultPort(), host, requestURI, headers, version); } @Override public WebSocketStream websocketStream(RequestOptions options, MultiMap headers, WebsocketVersion version, String subProtocols) { return new WebSocketStream(options.getPort(), options.getHost(), options.getURI(), headers, version, subProtocols, options.isSsl()); } @Override public WebSocketStream websocketStream(int port, String host, String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols) { return new WebSocketStream(port, host, requestURI, headers, version, subProtocols, null); } @Override public WebSocketStream websocketStream(String host, String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols) { return websocketStream(options.getDefaultPort(), host, requestURI, headers, version, subProtocols); } @Override public WebSocketStream websocketStream(String requestURI) { return websocketStream(options.getDefaultPort(), options.getDefaultHost(), requestURI); } @Override public WebSocketStream websocketStream(String requestURI, MultiMap headers) { return websocketStream(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers); } @Override public WebSocketStream websocketStream(String requestURI, MultiMap headers, WebsocketVersion version) { return websocketStream(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, version); } @Override public WebSocketStream websocketStream(String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols) { return websocketStream(options.getDefaultPort(), options.getDefaultHost(), requestURI, headers, version, subProtocols); } @Override public HttpClientRequest requestAbs(HttpMethod method, String absoluteURI, Handler<HttpClientResponse> responseHandler) { Objects.requireNonNull(responseHandler, "no null responseHandler accepted"); return requestAbs(method, absoluteURI).handler(responseHandler); } @Override public HttpClientRequest get(RequestOptions options) { return request(HttpMethod.GET, options); } @Override public HttpClientRequest request(HttpMethod method, int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { Objects.requireNonNull(responseHandler, "no null responseHandler accepted"); return request(method, port, host, requestURI).handler(responseHandler); } @Override public HttpClientRequest request(HttpMethod method, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(method, options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClientRequest request(HttpMethod method, String requestURI) { return request(method, options.getDefaultPort(), options.getDefaultHost(), requestURI); } @Override public HttpClientRequest request(HttpMethod method, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(method, options.getDefaultPort(), options.getDefaultHost(), requestURI, responseHandler); } @Override public HttpClientRequest requestAbs(HttpMethod method, String absoluteURI) { URL url = parseUrl(absoluteURI); Boolean ssl = false; int port = url.getPort(); String protocol = url.getProtocol(); if ("ftp".equals(protocol)) { if (port == -1) { port = 21; } } else { char chend = protocol.charAt(protocol.length() - 1); if (chend == 'p') { if (port == -1) { port = 80; } } else if (chend == 's'){ ssl = true; if (port == -1) { port = 443; } } } // if we do not know the protocol, the port still may be -1, we will handle that below return createRequest(method, protocol, 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); } @Override public HttpClientRequest request(HttpMethod method, RequestOptions options, Handler<HttpClientResponse> responseHandler) { return request(method, options).handler(responseHandler); } @Override public HttpClientRequest request(HttpMethod method, RequestOptions options) { return createRequest(method, options.getHost(), options.getPort(), options.isSsl(), options.getURI(), null); } @Override public HttpClientRequest request(HttpMethod method, String host, String requestURI) { return request(method, options.getDefaultPort(), host, requestURI); } @Override public HttpClientRequest get(int port, String host, String requestURI) { return request(HttpMethod.GET, port, host, requestURI); } @Override public HttpClientRequest get(String host, String requestURI) { return get(options.getDefaultPort(), host, requestURI); } @Override public HttpClientRequest get(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.GET, options, responseHandler); } @Override public HttpClientRequest get(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.GET, port, host, requestURI, responseHandler); } @Override public HttpClientRequest get(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return get(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClientRequest get(String requestURI) { return request(HttpMethod.GET, requestURI); } @Override public HttpClientRequest get(String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.GET, requestURI, responseHandler); } @Override public HttpClientRequest getAbs(String absoluteURI) { return requestAbs(HttpMethod.GET, absoluteURI); } @Override public HttpClientRequest getAbs(String absoluteURI, Handler<HttpClientResponse> responseHandler) { return requestAbs(HttpMethod.GET, absoluteURI, responseHandler); } @Override public HttpClient getNow(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return requestNow(HttpMethod.GET, options, responseHandler); } @Override public HttpClient getNow(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { get(port, host, requestURI, responseHandler).end(); return this; } @Override public HttpClient getNow(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return getNow(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClient getNow(String requestURI, Handler<HttpClientResponse> responseHandler) { get(requestURI, responseHandler).end(); return this; } @Override public HttpClientRequest post(RequestOptions options) { return request(HttpMethod.POST, options); } @Override public HttpClientRequest post(int port, String host, String requestURI) { return request(HttpMethod.POST, port, host, requestURI); } @Override public HttpClientRequest post(String host, String requestURI) { return post(options.getDefaultPort(), host, requestURI); } @Override public HttpClientRequest post(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.POST, options, responseHandler); } @Override public HttpClientRequest post(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.POST, port, host, requestURI, responseHandler); } @Override public HttpClientRequest post(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return post(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClientRequest post(String requestURI) { return request(HttpMethod.POST, requestURI); } @Override public HttpClientRequest post(String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.POST, requestURI, responseHandler); } @Override public HttpClientRequest postAbs(String absoluteURI) { return requestAbs(HttpMethod.POST, absoluteURI); } @Override public HttpClientRequest postAbs(String absoluteURI, Handler<HttpClientResponse> responseHandler) { return requestAbs(HttpMethod.POST, absoluteURI, responseHandler); } @Override public HttpClientRequest head(RequestOptions options) { return request(HttpMethod.HEAD, options); } @Override public HttpClientRequest head(int port, String host, String requestURI) { return request(HttpMethod.HEAD, port, host, requestURI); } @Override public HttpClientRequest head(String host, String requestURI) { return head(options.getDefaultPort(), host, requestURI); } @Override public HttpClientRequest head(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.HEAD, options, responseHandler); } @Override public HttpClientRequest head(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.HEAD, port, host, requestURI, responseHandler); } @Override public HttpClientRequest head(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return head(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClientRequest head(String requestURI) { return request(HttpMethod.HEAD, requestURI); } @Override public HttpClientRequest head(String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.HEAD, requestURI, responseHandler); } @Override public HttpClientRequest headAbs(String absoluteURI) { return requestAbs(HttpMethod.HEAD, absoluteURI); } @Override public HttpClientRequest headAbs(String absoluteURI, Handler<HttpClientResponse> responseHandler) { return requestAbs(HttpMethod.HEAD, absoluteURI, responseHandler); } @Override public HttpClient headNow(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return requestNow(HttpMethod.HEAD, options, responseHandler); } @Override public HttpClient headNow(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { head(port, host, requestURI, responseHandler).end(); return this; } @Override public HttpClient headNow(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return headNow(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClient headNow(String requestURI, Handler<HttpClientResponse> responseHandler) { head(requestURI, responseHandler).end(); return this; } @Override public HttpClientRequest options(RequestOptions options) { return request(HttpMethod.OPTIONS, options); } @Override public HttpClientRequest options(int port, String host, String requestURI) { return request(HttpMethod.OPTIONS, port, host, requestURI); } @Override public HttpClientRequest options(String host, String requestURI) { return options(options.getDefaultPort(), host, requestURI); } @Override public HttpClientRequest options(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.OPTIONS, options, responseHandler); } @Override public HttpClientRequest options(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.OPTIONS, port, host, requestURI, responseHandler); } @Override public HttpClientRequest options(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return options(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClientRequest options(String requestURI) { return request(HttpMethod.OPTIONS, requestURI); } @Override public HttpClientRequest options(String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.OPTIONS, requestURI, responseHandler); } @Override public HttpClientRequest optionsAbs(String absoluteURI) { return requestAbs(HttpMethod.OPTIONS, absoluteURI); } @Override public HttpClientRequest optionsAbs(String absoluteURI, Handler<HttpClientResponse> responseHandler) { return requestAbs(HttpMethod.OPTIONS, absoluteURI, responseHandler); } @Override public HttpClient optionsNow(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return requestNow(HttpMethod.OPTIONS, options, responseHandler); } @Override public HttpClient optionsNow(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { options(port, host, requestURI, responseHandler).end(); return this; } @Override public HttpClient optionsNow(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return optionsNow(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClient optionsNow(String requestURI, Handler<HttpClientResponse> responseHandler) { options(requestURI, responseHandler).end(); return this; } @Override public HttpClientRequest put(RequestOptions options) { return request(HttpMethod.PUT, options); } @Override public HttpClientRequest put(int port, String host, String requestURI) { return request(HttpMethod.PUT, port, host, requestURI); } @Override public HttpClientRequest put(String host, String requestURI) { return put(options.getDefaultPort(), host, requestURI); } @Override public HttpClientRequest put(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.PUT, options, responseHandler); } @Override public HttpClientRequest put(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.PUT, port, host, requestURI, responseHandler); } @Override public HttpClientRequest put(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return put(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClientRequest put(String requestURI) { return request(HttpMethod.PUT, requestURI); } @Override public HttpClientRequest put(String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.PUT, requestURI, responseHandler); } @Override public HttpClientRequest putAbs(String absoluteURI) { return requestAbs(HttpMethod.PUT, absoluteURI); } @Override public HttpClientRequest putAbs(String absoluteURI, Handler<HttpClientResponse> responseHandler) { return requestAbs(HttpMethod.PUT, absoluteURI, responseHandler); } @Override public HttpClientRequest delete(RequestOptions options) { return request(HttpMethod.DELETE, options); } @Override public HttpClientRequest delete(int port, String host, String requestURI) { return request(HttpMethod.DELETE, port, host, requestURI); } @Override public HttpClientRequest delete(String host, String requestURI) { return delete(options.getDefaultPort(), host, requestURI); } @Override public HttpClientRequest delete(RequestOptions options, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.DELETE, options, responseHandler); } @Override public HttpClientRequest delete(int port, String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.DELETE, port, host, requestURI, responseHandler); } @Override public HttpClientRequest delete(String host, String requestURI, Handler<HttpClientResponse> responseHandler) { return delete(options.getDefaultPort(), host, requestURI, responseHandler); } @Override public HttpClientRequest delete(String requestURI) { return request(HttpMethod.DELETE, requestURI); } @Override public HttpClientRequest delete(String requestURI, Handler<HttpClientResponse> responseHandler) { return request(HttpMethod.DELETE, requestURI, responseHandler); } @Override public HttpClientRequest deleteAbs(String absoluteURI) { return requestAbs(HttpMethod.DELETE, absoluteURI); } @Override public HttpClientRequest deleteAbs(String absoluteURI, Handler<HttpClientResponse> responseHandler) { return requestAbs(HttpMethod.DELETE, absoluteURI, responseHandler); } @Override public synchronized void close() { synchronized (this) { checkClosed(); closed = true; } if (creatingContext != null) { creatingContext.removeCloseHook(closeHook); } connectionManager.close(); } @Override public boolean isMetricsEnabled() { return getMetrics().isEnabled(); } @Override public Metrics getMetrics() { return connectionManager.metrics(); } @Override public HttpClient redirectHandler(Function<HttpClientResponse, Future<HttpClientRequest>> handler) { if (handler == null) { handler = DEFAULT_HANDLER; } redirectHandler = handler; return this; } @Override public Function<HttpClientResponse, Future<HttpClientRequest>> redirectHandler() { return redirectHandler; } public HttpClientOptions getOptions() { return options; } void getConnectionForWebsocket(boolean ssl, int port, String host, Handler<ClientConnection> handler, Handler<Throwable> connectionExceptionHandler, ContextImpl context) { connectionManager.getConnectionForWebsocket(ssl, port, host, new Waiter(null, context) { @Override void handleConnection(HttpClientConnection conn) { } @Override void handleStream(HttpClientStream stream) { // Use some variance for this handler.handle((ClientConnection) stream); } @Override void handleFailure(Throwable failure) { connectionExceptionHandler.handle(failure); } @Override boolean isCancelled() { return false; } }); } void getConnectionForRequest(String peerHost, boolean ssl, int port, String host, Waiter waiter) { connectionManager.getConnectionForRequest(options.getProtocolVersion(), peerHost, ssl, port, host, waiter); } /** * @return the vertx, for use in package related classes only. */ VertxInternal getVertx() { return vertx; } SSLHelper getSslHelper() { return sslHelper; } HttpClientMetrics httpClientMetrics() { return connectionManager.metrics(); } private URL parseUrl(String surl) { // Note - parsing a URL this way is slower than specifying host, port and relativeURI try { return new URL(surl); } catch (MalformedURLException e) { throw new VertxException("Invalid url: " + surl, e); } } private HttpClient requestNow(HttpMethod method, RequestOptions options, Handler<HttpClientResponse> responseHandler) { createRequest(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) { return createRequest(method, ssl==null || ssl==false ? "http" : "https", host, port, ssl, relativeURI, headers); } private HttpClientRequest createRequest(HttpMethod method, String protocol, String host, int port, Boolean ssl, String relativeURI, MultiMap headers) { Objects.requireNonNull(method, "no null method accepted"); Objects.requireNonNull(protocol, "no null protocol accepted"); Objects.requireNonNull(host, "no null host accepted"); Objects.requireNonNull(relativeURI, "no null relativeURI accepted"); checkClosed(); HttpClientRequest req; boolean useSSL = ssl != null ? ssl : options.isSsl(); boolean useProxy = !useSSL && proxyType == ProxyType.HTTP; if (useProxy) { final int defaultPort = protocol.equals("ftp") ? 21 : 80; final String addPort = (port != -1 && port != defaultPort) ? (":" + port) : ""; relativeURI = protocol + "://" + host + addPort + relativeURI; ProxyOptions proxyOptions = options.getProxyOptions(); if (proxyOptions.getUsername() != null && proxyOptions.getPassword() != null) { if (headers == null) { headers = MultiMap.caseInsensitiveMultiMap(); } headers.add("Proxy-Authorization", "Basic " + Base64.getEncoder() .encodeToString((proxyOptions.getUsername() + ":" + proxyOptions.getPassword()).getBytes())); } req = new HttpClientRequestImpl(this, useSSL, method, proxyOptions.getHost(), proxyOptions.getPort(), relativeURI, vertx); req.setHost(host + addPort); } else { req = new HttpClientRequestImpl(this, useSSL, method, host, port, relativeURI, vertx); } if (headers != null) { req.headers().setAll(headers); } return req; } private synchronized void checkClosed() { if (closed) { throw new IllegalStateException("Client is closed"); } } private class WebSocketStream implements ReadStream<WebSocket> { final int port; final String host; final String requestURI; final MultiMap headers; final WebsocketVersion version; final String subProtocols; private Handler<WebSocket> handler; private Handler<Throwable> exceptionHandler; private Handler<Void> endHandler; private Boolean ssl; WebSocketStream(int port, String host, String requestURI, MultiMap headers, WebsocketVersion version, String subProtocols, Boolean ssl) { this.port = port; this.host = host; this.requestURI = requestURI; this.headers = headers; this.version = version; this.subProtocols = subProtocols; this.ssl = ssl; } @Override public synchronized ReadStream<WebSocket> exceptionHandler(Handler<Throwable> handler) { exceptionHandler = handler; return this; } @Override public synchronized ReadStream<WebSocket> handler(Handler<WebSocket> handler) { if (this.handler == null && handler != null) { this.handler = handler; checkClosed(); ContextImpl context = vertx.getOrCreateContext(); Handler<Throwable> connectionExceptionHandler; if (exceptionHandler == null) { connectionExceptionHandler = log::error; } else { connectionExceptionHandler = exceptionHandler; } Handler<WebSocket> wsConnect; if (endHandler != null) { Handler<Void> endCallback = endHandler; wsConnect = ws -> { handler.handle(ws); endCallback.handle(null); }; } else { wsConnect = handler; } getConnectionForWebsocket(ssl != null ? ssl : options.isSsl(), port, host, conn -> { conn.exceptionHandler(connectionExceptionHandler); if (conn.isValid()) { conn.toWebSocket(requestURI, headers, version, subProtocols, options.getMaxWebsocketFrameSize(), wsConnect); } else { websocket(port, host, requestURI, headers, version, subProtocols, wsConnect); } }, connectionExceptionHandler, context); } return this; } @Override public synchronized ReadStream<WebSocket> endHandler(Handler<Void> endHandler) { this.endHandler = endHandler; return this; } @Override public ReadStream<WebSocket> pause() { return this; } @Override public ReadStream<WebSocket> resume() { return this; } } @Override protected void finalize() throws Throwable { // Make sure this gets cleaned up if there are no more references to it // so as not to leave connections and resources dangling until the system is shutdown // which could make the JVM run out of file handles. close(); super.finalize(); } }