diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/ConnectionHandler.java b/webserver/webserver/src/main/java/io/helidon/webserver/ConnectionHandler.java index ac8d121830a..c4bb7ab11b0 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/ConnectionHandler.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/ConnectionHandler.java @@ -17,19 +17,26 @@ package io.helidon.webserver; import java.io.UncheckedIOException; +import java.net.Socket; +import java.util.HexFormat; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; +import javax.net.ssl.SSLSocket; + import io.helidon.common.buffers.BufferData; import io.helidon.common.buffers.DataReader; import io.helidon.common.buffers.DataWriter; import io.helidon.common.socket.HelidonSocket; import io.helidon.common.socket.PeerInfo; +import io.helidon.common.socket.PlainSocket; import io.helidon.common.socket.SocketWriter; +import io.helidon.common.socket.TlsSocket; import io.helidon.common.task.InterruptableTask; +import io.helidon.common.tls.Tls; import io.helidon.http.HttpException; import io.helidon.http.RequestException; import io.helidon.webserver.spi.ServerConnection; @@ -53,20 +60,25 @@ class ConnectionHandler implements InterruptableTask, ConnectionContext { private final ConnectionProviders connectionProviders; private final List providerCandidates; private final Map activeConnections; - private final HelidonSocket socket; + private final Socket socket; + private final String serverChannelId; private final Router router; - private final SocketWriter writer; - private final DataReader reader; + private final Tls tls; private ServerConnection connection; + private HelidonSocket helidonSocket; + private DataReader reader; + private SocketWriter writer; ConnectionHandler(ListenerContext listenerContext, Semaphore connectionSemaphore, Semaphore requestSemaphore, ConnectionProviders connectionProviders, Map activeConnections, - HelidonSocket socket, - Router router) { + Socket socket, + String serverChannelId, + Router router, + Tls tls) { this.listenerContext = listenerContext; this.connectionSemaphore = connectionSemaphore; this.requestSemaphore = requestSemaphore; @@ -74,9 +86,9 @@ class ConnectionHandler implements InterruptableTask, ConnectionContext { this.providerCandidates = connectionProviders.providerCandidates(); this.activeConnections = activeConnections; this.socket = socket; + this.serverChannelId = serverChannelId; this.router = router; - this.writer = SocketWriter.create(listenerContext.executor(), socket, listenerContext.config().writeQueueLength()); - this.reader = new DataReader(socket); + this.tls = tls; } @Override @@ -86,19 +98,49 @@ public boolean canInterrupt() { @Override public final void run() { - String socketsId = socket.socketId() + " " + socket.childSocketId(); + String channelId = "0x" + HexFormat.of().toHexDigits(System.identityHashCode(socket)); + + // handle SSL and init helidonSocket, reader and writer + try { + if (tls.enabled()) { + SSLSocket sslSocket = (SSLSocket) socket; + sslSocket.setHandshakeApplicationProtocolSelector( + (sslEngine, list) -> { + for (String protocolId : list) { + if (connectionProviders.supportedApplicationProtocols() + .contains(protocolId)) { + return protocolId; + } + } + return null; + }); + sslSocket.startHandshake(); + helidonSocket = TlsSocket.server(sslSocket, channelId, serverChannelId); + } else { + helidonSocket = PlainSocket.server(socket, channelId, serverChannelId); + } + + reader = new DataReader(helidonSocket); + writer = SocketWriter.create(listenerContext.executor(), helidonSocket, + listenerContext.config().writeQueueLength()); + } catch (Exception e) { + throw e instanceof RuntimeException re ? re : new RuntimeException(e); // see ServerListener + } + + // connection handling + String socketsId = helidonSocket.socketId() + " " + helidonSocket.childSocketId(); Thread.currentThread().setName("[" + socketsId + "] WebServer socket"); if (LOGGER.isLoggable(DEBUG)) { - socket.log(LOGGER, + helidonSocket.log(LOGGER, DEBUG, "accepted socket from %s:%d", - socket.remotePeer().host(), - socket.remotePeer().port()); + helidonSocket.remotePeer().host(), + helidonSocket.remotePeer().port()); } try { - if (socket.protocolNegotiated()) { - this.connection = connectionProviders.byApplicationProtocol(socket.protocol()) + if (helidonSocket.protocolNegotiated()) { + this.connection = connectionProviders.byApplicationProtocol(helidonSocket.protocol()) .connection(this); } @@ -112,17 +154,17 @@ public final void run() { activeConnections.put(socketsId, connection); connection.handle(requestSemaphore); } catch (RequestException e) { - socket.log(LOGGER, WARNING, "escaped Request exception", e); + helidonSocket.log(LOGGER, WARNING, "escaped Request exception", e); } catch (HttpException e) { - socket.log(LOGGER, WARNING, "escaped HTTP exception", e); + helidonSocket.log(LOGGER, WARNING, "escaped HTTP exception", e); } catch (CloseConnectionException e) { // end of request stream - safe to close the connection, as it was requested by our client - socket.log(LOGGER, TRACE, "connection close requested", e); + helidonSocket.log(LOGGER, TRACE, "connection close requested", e); } catch (UncheckedIOException e) { // socket exception - the socket failed, probably killed by OS, proxy or client - socket.log(LOGGER, TRACE, "received I/O exception", e); + helidonSocket.log(LOGGER, TRACE, "received I/O exception", e); } catch (Exception e) { - socket.log(LOGGER, WARNING, "unexpected exception", e); + helidonSocket.log(LOGGER, WARNING, "unexpected exception", e); } finally { // connection has finished the loop of handling, release the semaphore connectionSemaphore.release(); @@ -131,32 +173,32 @@ public final void run() { closeChannel(); } - socket.log(LOGGER, DEBUG, "socket closed"); + helidonSocket.log(LOGGER, DEBUG, "socket closed"); } @Override public PeerInfo remotePeer() { - return socket.remotePeer(); + return helidonSocket.remotePeer(); } @Override public PeerInfo localPeer() { - return socket.localPeer(); + return helidonSocket.localPeer(); } @Override public boolean isSecure() { - return socket.isSecure(); + return helidonSocket.isSecure(); } @Override public String socketId() { - return socket.socketId(); + return helidonSocket.socketId(); } @Override public String childSocketId() { - return socket.childSocketId(); + return helidonSocket.childSocketId(); } @Override @@ -198,7 +240,7 @@ private ServerConnection identifyConnection() { while (true) { Iterator iterator = providerCandidates.iterator(); if (!iterator.hasNext()) { - socket.log(LOGGER, DEBUG, "Could not find a suitable connection provider. " + helidonSocket.log(LOGGER, DEBUG, "Could not find a suitable connection provider. " + "initial connection buffer (may be empty if no providers exist):\n%s", currentBuffer.debugDataHex(false)); return null; @@ -240,7 +282,7 @@ private ServerConnection identifyConnection() { // we may have removed all candidates, we must re-check // we must return before requesting more data (as more data may not be available) if (providerCandidates.isEmpty()) { - socket.log(LOGGER, + helidonSocket.log(LOGGER, DEBUG, "Could not find a suitable connection provider. " + "initial connection buffer (may be empty if no providers exist):\n%s", @@ -256,9 +298,9 @@ private ServerConnection identifyConnection() { private void closeChannel() { try { - socket.close(); + helidonSocket.close(); } catch (Throwable e) { - socket.log(LOGGER, TRACE, "Failed to close socket on connection close", e); + helidonSocket.log(LOGGER, TRACE, "Failed to close socket on connection close", e); } } } diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/ServerListener.java b/webserver/webserver/src/main/java/io/helidon/webserver/ServerListener.java index bd4b27c6d56..e5862e3423e 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/ServerListener.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/ServerListener.java @@ -42,15 +42,11 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLSocket; import io.helidon.common.HelidonServiceLoader; import io.helidon.common.LazyValue; import io.helidon.common.context.Context; -import io.helidon.common.socket.HelidonSocket; -import io.helidon.common.socket.PlainSocket; import io.helidon.common.socket.SocketOptions; -import io.helidon.common.socket.TlsSocket; import io.helidon.common.task.HelidonTaskExecutor; import io.helidon.common.tls.Tls; import io.helidon.http.encoding.ContentEncodingContext; @@ -71,10 +67,8 @@ class ServerListener implements ListenerContext { private static final System.Logger LOGGER = System.getLogger(ServerListener.class.getName()); @SuppressWarnings("rawtypes") - private static final LazyValue> SELECTOR_PROVIDERS = LazyValue.create(() -> { - return HelidonServiceLoader.create(ServiceLoader.load(ServerConnectionSelectorProvider.class)) - .asList(); - }); + private static final LazyValue> SELECTOR_PROVIDERS = LazyValue.create(() -> + HelidonServiceLoader.create(ServiceLoader.load(ServerConnectionSelectorProvider.class)).asList()); private final ConnectionProviders connectionProviders; private final String socketName; @@ -359,37 +353,16 @@ private void listen() { Socket socket = serverSocket.accept(); try { - ConnectionHandler handler; - String channelId = "0x" + HexFormat.of().toHexDigits(System.identityHashCode(socket)); connectionOptions.configureSocket(socket); - - HelidonSocket helidonSocket; - if (tls.enabled()) { - SSLSocket sslSocket = (SSLSocket) socket; - sslSocket.setHandshakeApplicationProtocolSelector( - (sslEngine, list) -> { - for (String protocolId : list) { - if (connectionProviders.supportedApplicationProtocols() - .contains(protocolId)) { - return protocolId; - } - } - return null; - }); - sslSocket.startHandshake(); - helidonSocket = TlsSocket.server(sslSocket, channelId, serverChannelId); - } else { - helidonSocket = PlainSocket.server(socket, channelId, serverChannelId); - } - - handler = new ConnectionHandler(this, + ConnectionHandler handler = new ConnectionHandler(this, connectionSemaphore, requestSemaphore, connectionProviders, activeConnections, - helidonSocket, - router); - + socket, + serverChannelId, + router, + tls); readerExecutor.execute(handler); } catch (RejectedExecutionException e) { LOGGER.log(ERROR, "Executor rejected handler for new connection");