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 | |
parent | 386edaaa02e13489c7a53249c1f3c13b52724766 (diff) |
Support proxying of health checks to connector requiring proxy-protocol
Use Jetty instead of Apache http client.
Install jetty-client bundle.
-rw-r--r-- | cloud-tenant-base-dependencies-enforcer/pom.xml | 1 | ||||
-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 | ||||
-rw-r--r-- | container-disc/pom.xml | 1 | ||||
-rw-r--r-- | jdisc_jetty/pom.xml | 4 | ||||
-rw-r--r-- | parent/pom.xml | 5 |
6 files changed, 51 insertions, 47 deletions
diff --git a/cloud-tenant-base-dependencies-enforcer/pom.xml b/cloud-tenant-base-dependencies-enforcer/pom.xml index b8c08843c9a..04fda62fceb 100644 --- a/cloud-tenant-base-dependencies-enforcer/pom.xml +++ b/cloud-tenant-base-dependencies-enforcer/pom.xml @@ -252,6 +252,7 @@ <include>org.eclipse.jetty.http2:http2-server:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-alpn-server:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-alpn-java-server:[${jetty.version}]:jar:test</include> + <include>org.eclipse.jetty:jetty-client:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-continuation:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-jmx:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-security:[${jetty.version}]:jar:test</include> 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; } } diff --git a/container-disc/pom.xml b/container-disc/pom.xml index f84e1868724..a94225f6b5e 100644 --- a/container-disc/pom.xml +++ b/container-disc/pom.xml @@ -227,6 +227,7 @@ http2-hpack-${jetty.version}.jar, jetty-alpn-java-server-${jetty.version}.jar, jetty-alpn-server-${jetty.version}.jar, + jetty-client-${jetty.version}.jar, jetty-continuation-${jetty.version}.jar, jetty-http-${jetty.version}.jar, jetty-io-${jetty.version}.jar, diff --git a/jdisc_jetty/pom.xml b/jdisc_jetty/pom.xml index ea4aa82cb15..76140f67416 100644 --- a/jdisc_jetty/pom.xml +++ b/jdisc_jetty/pom.xml @@ -29,6 +29,10 @@ </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-client</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-continuation</artifactId> </dependency> <dependency> diff --git a/parent/pom.xml b/parent/pom.xml index acd1ed6f25d..f232e870db5 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -763,6 +763,11 @@ </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-client</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-continuation</artifactId> <version>${jetty.version}</version> </dependency> |