Skip to content

Commit 3e46173

Browse files
committed
fix issue Dispatcher thread stuck in SSL Handshake, httpserver not anymore responsive robaho#23
1 parent 0043b0a commit 3e46173

File tree

1 file changed

+76
-65
lines changed

1 file changed

+76
-65
lines changed

src/main/java/robaho/net/httpserver/ServerImpl.java

Lines changed: 76 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import java.util.concurrent.Executor;
5151
import java.util.concurrent.ExecutorService;
5252
import java.util.concurrent.Executors;
53+
import java.util.concurrent.RejectedExecutionException;
5354
import java.util.logging.LogRecord;
5455

5556
import javax.net.ssl.SSLSocket;
@@ -342,82 +343,92 @@ public void run() {
342343
while (true) {
343344
try {
344345
Socket s = socket.accept();
345-
if(logger.isLoggable(Level.TRACE)) {
346-
logger.log(Level.TRACE, "accepted connection: " + s.toString());
347-
}
348-
stats.connectionCount.incrementAndGet();
349-
if (MAX_CONNECTIONS > 0 && allConnections.size() >= MAX_CONNECTIONS) {
350-
// we've hit max limit of current open connections, so we go
351-
// ahead and close this connection without processing it
346+
try {
347+
executor.execute(() -> {
352348
try {
353-
stats.maxConnectionsExceededCount.incrementAndGet();
354-
logger.log(Level.WARNING, "closing accepted connection due to too many connections");
355-
s.close();
356-
} catch (IOException ignore) {
349+
acceptConnection(s);
350+
} catch (IOException t) {
351+
logger.log(Level.ERROR, "Dispatcher Exception", t);
352+
try {
353+
s.close();
354+
} catch (IOException ex) {
355+
}
357356
}
358-
continue;
357+
});
358+
} catch (RejectedExecutionException e) {
359+
s.close();
359360
}
360-
361-
if (ServerConfig.noDelay()) {
362-
s.setTcpNoDelay(true);
361+
} catch (IOException e) {
362+
if (!isFinishing()) {
363+
logger.log(Level.ERROR, "Dispatcher Exception, terminating", e);
363364
}
365+
return;
366+
}
367+
}
368+
}
369+
private void acceptConnection(Socket s) throws IOException {
370+
if(logger.isLoggable(Level.TRACE)) {
371+
logger.log(Level.TRACE, "accepted connection: " + s.toString());
372+
}
373+
stats.connectionCount.incrementAndGet();
374+
if (MAX_CONNECTIONS > 0 && allConnections.size() >= MAX_CONNECTIONS) {
375+
// we've hit max limit of current open connections, so we go
376+
// ahead and close this connection without processing it
377+
try {
378+
stats.maxConnectionsExceededCount.incrementAndGet();
379+
logger.log(Level.WARNING, "closing accepted connection due to too many connections");
380+
s.close();
381+
} catch (IOException ignore) {
382+
}
383+
return;
384+
}
364385

365-
boolean http2 = false;
386+
if (ServerConfig.noDelay()) {
387+
s.setTcpNoDelay(true);
388+
}
366389

367-
if (https) {
368-
// for some reason, creating an SSLServerSocket and setting the default parameters would
369-
// not work, so upgrade to a SSLSocket after connection
370-
SSLSocketFactory ssf = httpsConfig.getSSLContext().getSocketFactory();
371-
SSLSocket sslSocket = (SSLSocket) ssf.createSocket(s, null, false);
372-
SSLConfigurator.configure(sslSocket,httpsConfig);
390+
boolean http2 = false;
373391

374-
sslSocket.setHandshakeApplicationProtocolSelector((_sslSocket, protocols) -> {
375-
if (protocols.contains("h2") && ServerConfig.http2OverSSL()) {
376-
return "h2";
377-
} else {
378-
return "http/1.1";
379-
}
380-
});
381-
// the following forces the SSL handshake to complete in order to determine the negotiated protocol
382-
var session = sslSocket.getSession();
383-
if ("h2".equals(sslSocket.getApplicationProtocol())) {
384-
logger.log(Level.DEBUG, () -> "http2 connection "+sslSocket.toString());
385-
http2 = true;
386-
} else {
387-
logger.log(Level.DEBUG, () -> "http/1.1 connection "+sslSocket.toString());
388-
}
389-
s = sslSocket;
392+
if (https) {
393+
// for some reason, creating an SSLServerSocket and setting the default parameters would
394+
// not work, so upgrade to a SSLSocket after connection
395+
SSLSocketFactory ssf = httpsConfig.getSSLContext().getSocketFactory();
396+
SSLSocket sslSocket = (SSLSocket) ssf.createSocket(s, null, false);
397+
SSLConfigurator.configure(sslSocket,httpsConfig);
398+
399+
sslSocket.setHandshakeApplicationProtocolSelector((_sslSocket, protocols) -> {
400+
if (protocols.contains("h2") && ServerConfig.http2OverSSL()) {
401+
return "h2";
402+
} else {
403+
return "http/1.1";
390404
}
405+
});
406+
// the following forces the SSL handshake to complete in order to determine the negotiated protocol
407+
var session = sslSocket.getSession();
408+
if ("h2".equals(sslSocket.getApplicationProtocol())) {
409+
logger.log(Level.DEBUG, () -> "http2 connection "+sslSocket.toString());
410+
http2 = true;
411+
} else {
412+
logger.log(Level.DEBUG, () -> "http/1.1 connection "+sslSocket.toString());
413+
}
414+
s = sslSocket;
415+
}
391416

392-
HttpConnection c;
393-
try {
394-
c = new HttpConnection(s);
395-
} catch (IOException e) {
396-
logger.log(Level.WARNING, "Failed to create HttpConnection", e);
397-
continue;
398-
}
399-
try {
400-
allConnections.add(c);
401-
402-
if (http2) {
403-
Http2Exchange t = new Http2Exchange(protocol, c);
404-
executor.execute(t);
405-
} else {
406-
Exchange t = new Exchange(protocol, c);
407-
executor.execute(t);
408-
}
417+
HttpConnection c = new HttpConnection(s);
418+
try {
419+
allConnections.add(c);
409420

410-
} catch (Exception e) {
411-
logger.log(Level.TRACE, "Dispatcher Exception", e);
412-
stats.handleExceptionCount.incrementAndGet();
413-
closeConnection(c);
414-
}
415-
} catch (IOException e) {
416-
if (!isFinishing()) {
417-
logger.log(Level.ERROR, "Dispatcher Exception, terminating", e);
418-
}
419-
return;
421+
if (http2) {
422+
Http2Exchange t = new Http2Exchange(protocol, c);
423+
executor.execute(t);
424+
} else {
425+
Exchange t = new Exchange(protocol, c);
426+
executor.execute(t);
420427
}
428+
} catch (Exception e) {
429+
logger.log(Level.TRACE, "Dispatcher Exception", e);
430+
stats.handleExceptionCount.incrementAndGet();
431+
closeConnection(c);
421432
}
422433
}
423434
}

0 commit comments

Comments
 (0)