diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2022-05-11 15:05:07 +0200 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2022-05-11 15:05:07 +0200 |
commit | e0c5916189f8e7d71c9998f7fafd5e51d9f58667 (patch) | |
tree | d4431cd6272fd3ebc7912e444b18198d91052e47 /container-core | |
parent | 386edaaa02e13489c7a53249c1f3c13b52724766 (diff) |
Support proxying of health checks to connector requiring proxy-protocol
Use Jetty instead of Apache http client.
Install jetty-client bundle.
Diffstat (limited to 'container-core')
-rw-r--r-- | container-core/pom.xml | 6 | ||||
-rw-r--r-- | container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java | 81 |
2 files changed, 40 insertions, 47 deletions
diff --git a/container-core/pom.xml b/container-core/pom.xml index be22e5cae5c..4b47cfde122 100644 --- a/container-core/pom.xml +++ b/container-core/pom.xml @@ -287,12 +287,6 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-client</artifactId> - <version>${jetty.version}</version> - <scope>test</scope> - </dependency> - <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <scope>test</scope> diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java index 5f55bcfe0b4..97ea30b3867 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java @@ -7,15 +7,11 @@ import com.yahoo.security.SslContextBuilder; import com.yahoo.security.tls.TransportSecurityOptions; import com.yahoo.security.tls.TransportSecurityUtils; import com.yahoo.security.tls.TrustAllX509TrustManager; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.util.EntityUtils; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.server.DetectorConnectionFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.SslConnectionFactory; @@ -86,7 +82,9 @@ class HealthCheckProxyHandler extends HandlerWrapper { .map(detectorConnFactory -> detectorConnFactory.getBean(SslConnectionFactory.class))) .map(connFactory -> (SslContextFactory.Server) connFactory.getSslContextFactory()) .orElseThrow(() -> new IllegalArgumentException("Health check proxy can only target https port")); - return new ProxyTarget(targetPort, targetTimeout, sslContextFactory); + ConnectorConfig.ProxyProtocol proxyProtocolCfg = targetConnector.connectorConfig().proxyProtocol(); + boolean proxyProtocol = proxyProtocolCfg.enabled() && !proxyProtocolCfg.mixedMode(); + return new ProxyTarget(targetPort, targetTimeout, sslContextFactory, proxyProtocol); } @Override @@ -161,14 +159,16 @@ class HealthCheckProxyHandler extends HandlerWrapper { private static class ProxyTarget implements AutoCloseable { final int port; final Duration timeout; - final SslContextFactory.Server sslContextFactory; - volatile CloseableHttpClient client; + final SslContextFactory.Server serverSsl; + final boolean proxyProtocol; + volatile HttpClient client; volatile StatusResponse lastResponse; - ProxyTarget(int port, Duration timeout, SslContextFactory.Server sslContextFactory) { + ProxyTarget(int port, Duration timeout, SslContextFactory.Server serverSsl, boolean proxyProtocol) { this.port = port; this.timeout = timeout; - this.sslContextFactory = sslContextFactory; + this.serverSsl = serverSsl; + this.proxyProtocol = proxyProtocol; } StatusResponse requestStatusHtml() { @@ -180,16 +180,17 @@ class HealthCheckProxyHandler extends HandlerWrapper { } private StatusResponse getStatusResponse() { - try (CloseableHttpResponse clientResponse = client().execute(new HttpGet("https://localhost:" + port + HEALTH_CHECK_PATH))) { - int statusCode = clientResponse.getStatusLine().getStatusCode(); - HttpEntity entity = clientResponse.getEntity(); - if (entity != null) { - Header contentTypeHeader = entity.getContentType(); - String contentType = contentTypeHeader != null ? contentTypeHeader.getValue() : null; - byte[] content = EntityUtils.toByteArray(entity); - return new StatusResponse(statusCode, contentType, content); + try { + var request = client().newRequest("https://localhost:" + port + HEALTH_CHECK_PATH); + if (proxyProtocol) { + request.tag(new ProxyProtocolClientConnectionFactory.V1.Tag()); + } + ContentResponse response = request.send(); + byte[] content = response.getContent(); + if (content != null && content.length > 0) { + return new StatusResponse(response.getStatus(), response.getMediaType(), content); } else { - return new StatusResponse(statusCode, null, null); + return new StatusResponse(response.getStatus(), null, null); } } catch (Exception e) { log.log(Level.FINE, e, () -> "Proxy request failed" + e.getMessage()); @@ -198,26 +199,23 @@ class HealthCheckProxyHandler extends HandlerWrapper { } // Client construction must be delayed to ensure that the SslContextFactory is started before calling getSslContext(). - private CloseableHttpClient client() { + private HttpClient client() throws Exception { if (client == null) { synchronized (this) { if (client == null) { int timeoutMillis = (int) timeout.toMillis(); - client = HttpClientBuilder.create() - .disableAutomaticRetries() - .setMaxConnPerRoute(4) - .setSSLContext(getSslContext(sslContextFactory)) - .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) // Certificate may not match "localhost" - .setUserTokenHandler(context -> null) // https://stackoverflow.com/a/42112034/1615280 - .setUserAgent("health-check-proxy-client") - .setDefaultRequestConfig( - RequestConfig.custom() - .setConnectTimeout(timeoutMillis) - .setConnectionRequestTimeout(timeoutMillis) - .setSocketTimeout(timeoutMillis) - .build()) - .build(); - } + SslContextFactory.Client clientSsl = new SslContextFactory.Client(); + clientSsl.setHostnameVerifier((__, ___) -> true); + clientSsl.setSslContext(getSslContext(serverSsl)); + HttpClient client = new HttpClient(clientSsl); + client.setMaxConnectionsPerDestination(4); + client.setConnectTimeout(timeoutMillis); + client.setStopTimeout(timeoutMillis); + client.setIdleTimeout(timeoutMillis); + client.setUserAgentField(new HttpField(HttpHeader.USER_AGENT, "health-check-proxy-client")); + client.start(); + this.client = client; + } } } return client; @@ -247,10 +245,11 @@ class HealthCheckProxyHandler extends HandlerWrapper { } @Override - public void close() throws IOException { + public void close() throws Exception { synchronized (this) { if (client != null) { - client.close(); + client.stop(); + client.destroy(); client = null; } } |