From c4e53c75bdc895749034b77d61c40ba5d2139fe7 Mon Sep 17 00:00:00 2001 From: Bjørn Christian Seime Date: Wed, 14 Apr 2021 14:44:10 +0200 Subject: Track protocol layers (with versions) in connection log Organize fields based on protocol --- .../container/logging/ConnectionLogEntry.java | 17 ++++++++++++ .../container/logging/JsonConnectionLogWriter.java | 32 ++++++++++++++++++---- .../http/server/jetty/JettyConnectionLogger.java | 22 +++++++++++++++ .../jdisc/http/server/jetty/HttpServerTest.java | 8 +++++- 4 files changed, 72 insertions(+), 7 deletions(-) (limited to 'container-core') diff --git a/container-core/src/main/java/com/yahoo/container/logging/ConnectionLogEntry.java b/container-core/src/main/java/com/yahoo/container/logging/ConnectionLogEntry.java index 6afe3b74329..5b30ce5963d 100644 --- a/container-core/src/main/java/com/yahoo/container/logging/ConnectionLogEntry.java +++ b/container-core/src/main/java/com/yahoo/container/logging/ConnectionLogEntry.java @@ -33,6 +33,8 @@ public class ConnectionLogEntry { private final Instant sslPeerNotAfter; private final String sslSniServerName; private final SslHandshakeFailure sslHandshakeFailure; + private final String httpProtocol; + private final String proxyProtocolVersion; private ConnectionLogEntry(Builder builder) { @@ -57,6 +59,8 @@ public class ConnectionLogEntry { this.sslPeerNotAfter = builder.sslPeerNotAfter; this.sslSniServerName = builder.sslSniServerName; this.sslHandshakeFailure = builder.sslHandshakeFailure; + this.httpProtocol = builder.httpProtocol; + this.proxyProtocolVersion = builder.proxyProtocolVersion; } public static Builder builder(UUID id, Instant timestamp) { @@ -84,6 +88,8 @@ public class ConnectionLogEntry { public Optional sslPeerNotAfter() { return Optional.ofNullable(sslPeerNotAfter); } public Optional sslSniServerName() { return Optional.ofNullable(sslSniServerName); } public Optional sslHandshakeFailure() { return Optional.ofNullable(sslHandshakeFailure); } + public Optional httpProtocol() { return Optional.ofNullable(httpProtocol); } + public Optional proxyProtocolVersion() { return Optional.ofNullable(proxyProtocolVersion); } public static class SslHandshakeFailure { private final String type; @@ -133,6 +139,8 @@ public class ConnectionLogEntry { private Instant sslPeerNotAfter; private String sslSniServerName; private SslHandshakeFailure sslHandshakeFailure; + private String httpProtocol; + private String proxyProtocolVersion; Builder(UUID id, Instant timestamp) { @@ -217,9 +225,18 @@ public class ConnectionLogEntry { this.sslHandshakeFailure = sslHandshakeFailure; return this; } + public Builder withHttpProtocol(String protocol) { + this.httpProtocol = protocol; + return this; + } + public Builder withProxyProtocolVersion(String version) { + this.proxyProtocolVersion = version; + return this; + } public ConnectionLogEntry build(){ return new ConnectionLogEntry(this); } + } } diff --git a/container-core/src/main/java/com/yahoo/container/logging/JsonConnectionLogWriter.java b/container-core/src/main/java/com/yahoo/container/logging/JsonConnectionLogWriter.java index 158d2ec4ea6..dfdc5f1b55a 100644 --- a/container-core/src/main/java/com/yahoo/container/logging/JsonConnectionLogWriter.java +++ b/container-core/src/main/java/com/yahoo/container/logging/JsonConnectionLogWriter.java @@ -33,12 +33,32 @@ class JsonConnectionLogWriter implements LogWriter { writeOptionalInteger(generator, "peerPort", unwrap(record.peerPort())); writeOptionalString(generator, "localAddress", unwrap(record.localAddress())); writeOptionalInteger(generator, "localPort", unwrap(record.localPort())); - writeOptionalString(generator, "remoteAddress", unwrap(record.remoteAddress())); - writeOptionalInteger(generator, "remotePort", unwrap(record.remotePort())); - writeOptionalLong(generator, "httpBytesReceived", unwrap(record.httpBytesReceived())); - writeOptionalLong(generator, "httpBytesSent", unwrap(record.httpBytesSent())); - writeOptionalLong(generator, "requests", unwrap(record.requests())); - writeOptionalLong(generator, "responses", unwrap(record.responses())); + + String proxyProtocolVersion = unwrap(record.proxyProtocolVersion()); + String proxyProtocolRemoteAddress = unwrap(record.remoteAddress()); + Integer proxyProtocolRemotePort = unwrap(record.remotePort()); + if (isAnyValuePresent(proxyProtocolVersion, proxyProtocolRemoteAddress, proxyProtocolRemotePort)) { + generator.writeObjectFieldStart("proxyProtocol"); + writeOptionalString(generator, "version", proxyProtocolVersion); + writeOptionalString(generator, "remoteAddress", proxyProtocolRemoteAddress); + writeOptionalInteger(generator, "remotePort", proxyProtocolRemotePort); + generator.writeEndObject(); + } + + String httpVersion = unwrap(record.httpProtocol()); + Long httpBytesReceived = unwrap(record.httpBytesReceived()); + Long httpBytesSent = unwrap(record.httpBytesSent()); + Long httpRequests = unwrap(record.requests()); + Long httpResponses = unwrap(record.responses()); + if (isAnyValuePresent(httpVersion, httpBytesReceived, httpBytesSent, httpRequests, httpResponses)) { + generator.writeObjectFieldStart("http"); + writeOptionalString(generator, "version", httpVersion); + writeOptionalLong(generator, "bytesReceived", httpBytesReceived); + writeOptionalLong(generator, "responses", httpResponses); + writeOptionalLong(generator, "bytesSent", httpBytesSent); + writeOptionalLong(generator, "requests", httpRequests); + generator.writeEndObject(); + } String sslProtocol = unwrap(record.sslProtocol()); String sslSessionId = unwrap(record.sslSessionId()); diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java index cd1ca490f61..e7cdb13425f 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java @@ -6,6 +6,7 @@ import com.yahoo.container.logging.ConnectionLogEntry; import com.yahoo.container.logging.ConnectionLogEntry.SslHandshakeFailure.ExceptionEntry; import com.yahoo.io.HexDump; import com.yahoo.jdisc.http.ServerConfig; +import org.eclipse.jetty.http2.server.HTTP2ServerConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.SocketChannelEndPoint; @@ -94,9 +95,18 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List info = ConnectionInfo.from(endpoint); connectionInfo.put(IdentityKey.of(endpoint), info); } + String connectionClassName = connection.getClass().getSimpleName(); // For hidden implementations of Connection if (connection instanceof SslConnection) { SSLEngine sslEngine = ((SslConnection) connection).getSSLEngine(); sslToConnectionInfo.put(IdentityKey.of(sslEngine), info); + } else if (connection instanceof HttpConnection) { + info.setHttpProtocol("HTTP/1.1"); + } else if (connection instanceof HTTP2ServerConnection) { + info.setHttpProtocol("HTTP/2.0"); + } else if (connectionClassName.endsWith("ProxyProtocolV1Connection")) { + info.setProxyProtocolVersion("v1"); + } else if (connectionClassName.endsWith("ProxyProtocolV2Connection")) { + info.setProxyProtocolVersion("v2"); } if (connection.getEndPoint() instanceof ProxyConnectionFactory.ProxyEndPoint) { InetSocketAddress remoteAddress = connection.getEndPoint().getRemoteAddress(); @@ -227,6 +237,8 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List private Date sslPeerNotAfter; private List sslSniServerNames; private SSLHandshakeException sslHandshakeException; + private String proxyProtocolVersion; + private String httpProtocol; private ConnectionInfo(UUID uuid, long createdAt, InetSocketAddress localAddress, InetSocketAddress peerAddress) { this.uuid = uuid; @@ -290,6 +302,10 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List return this; } + synchronized ConnectionInfo setHttpProtocol(String protocol) { this.httpProtocol = protocol; return this; } + + synchronized ConnectionInfo setProxyProtocolVersion(String version) { this.proxyProtocolVersion = version; return this; } + synchronized ConnectionLogEntry toLogEntry() { ConnectionLogEntry.Builder builder = ConnectionLogEntry.builder(uuid, Instant.ofEpochMilli(createdAt)); if (closedAt > 0) { @@ -348,6 +364,12 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List .orElse("UNKNOWN"); builder.withSslHandshakeFailure(new ConnectionLogEntry.SslHandshakeFailure(type, exceptionChain)); } + if (httpProtocol != null) { + builder.withHttpProtocol(httpProtocol); + } + if (proxyProtocolVersion != null) { + builder.withProxyProtocolVersion(proxyProtocolVersion); + } return builder.build(); } diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java index f5d77b53f12..d35434032ad 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java @@ -520,7 +520,9 @@ public class HttpServerTest { Path certificateFile = tmpFolder.newFile().toPath(); generatePrivateKeyAndCertificate(privateKeyFile, certificateFile); - TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.WANT); + MetricConsumerMock metricConsumer = new MetricConsumerMock(); + InMemoryConnectionLog connectionLog = new InMemoryConnectionLog(); + TestDriver driver = createSslTestDriver(certificateFile, privateKeyFile, metricConsumer, connectionLog); try (CloseableHttpAsyncClient client = createHttp2Client(certificateFile, privateKeyFile)) { String uri = "https://localhost:" + driver.server().getListenPort() + "/status.html"; SimpleHttpResponse response = client.execute(SimpleHttpRequests.get(uri), null).get(); @@ -528,6 +530,8 @@ public class HttpServerTest { assertEquals(OK, response.getCode()); } assertTrue(driver.close()); + ConnectionLogEntry entry = connectionLog.logEntries().get(0); + assertEquals("HTTP/2.0", entry.httpProtocol().get()); } @Test @@ -806,7 +810,9 @@ public class HttpServerTest { assertLogEntryHasRemote(requestLogMock.entries().get(1), proxiedRemoteAddress, proxiedRemotePort); Assertions.assertThat(connectionLog.logEntries()).hasSize(2); assertLogEntryHasRemote(connectionLog.logEntries().get(0), proxiedRemoteAddress, proxiedRemotePort); + assertEquals("v1", connectionLog.logEntries().get(0).proxyProtocolVersion().get()); assertLogEntryHasRemote(connectionLog.logEntries().get(1), proxiedRemoteAddress, proxiedRemotePort); + assertEquals("v2", connectionLog.logEntries().get(1).proxyProtocolVersion().get()); } @Test -- cgit v1.2.3