5151import java .util .concurrent .Executor ;
5252import java .util .concurrent .ExecutorService ;
5353import java .util .concurrent .Executors ;
54- import java .util .concurrent .atomic .AtomicLong ;
5554import java .util .logging .LogRecord ;
5655
5756import javax .net .ssl .SSLSocket ;
6867import robaho .net .httpserver .http2 .HTTP2Connection ;
6968import robaho .net .httpserver .http2 .HTTP2ErrorCode ;
7069import robaho .net .httpserver .http2 .HTTP2Exception ;
70+ import robaho .net .httpserver .http2 .HTTP2Stats ;
7171import robaho .net .httpserver .http2 .HTTP2Stream ;
7272
7373/**
@@ -119,13 +119,8 @@ class ServerImpl {
119119 private Thread dispatcherThread ;
120120
121121 // statistics
122- private final AtomicLong connectionCount = new AtomicLong ();
123- private final AtomicLong requestCount = new AtomicLong ();
124- private final AtomicLong handleExceptionCount = new AtomicLong ();
125- private final AtomicLong socketExceptionCount = new AtomicLong ();
126- private final AtomicLong idleCloseCount = new AtomicLong ();
127- private final AtomicLong replyErrorCount = new AtomicLong ();
128- private final AtomicLong maxConnectionsExceededCount = new AtomicLong ();
122+ private final ServerStats stats = new ServerStats ();
123+ private final HTTP2Stats http2Stats = new HTTP2Stats ();
129124
130125 ServerImpl (HttpServer wrapper , String protocol , InetSocketAddress addr , int backlog ) throws IOException {
131126
@@ -156,55 +151,41 @@ class ServerImpl {
156151 if (Boolean .getBoolean ("robaho.net.httpserver.EnableStats" )) {
157152 createContext ("/__stats" ,new StatsHandler ());
158153 }
154+ if (Boolean .getBoolean ("robaho.net.httpserver.EnableDebug" )) {
155+ createContext ("/__debug" ,new DebugHandler ());
156+ }
159157 }
160158
161159 private class StatsHandler implements HttpHandler {
162- volatile long lastStatsTime = System .currentTimeMillis ();
163- volatile long lastRequestCount = 0 ;
164160 @ Override
165161 public void handle (HttpExchange exchange ) throws IOException {
166-
167- long now = System .currentTimeMillis ();
168-
169- if ("reset" .equals (exchange .getRequestURI ().getQuery ())) {
170- connectionCount .set (0 );
171- requestCount .set (0 );
172- handleExceptionCount .set (0 );
173- socketExceptionCount .set (0 );
174- idleCloseCount .set (0 );
175- replyErrorCount .set (0 );
176- maxConnectionsExceededCount .set (0 );
177- lastStatsTime = now ;
178- lastRequestCount = 0 ;
179- exchange .sendResponseHeaders (200 ,-1 );
180- exchange .close ();
181- return ;
182- }
183-
184- var rc = requestCount .get ();
185-
186162 var output =
187163 (
188- "Connections: " +connectionCount .get ()+"\n " +
189164 "Active Connections: " +allConnections .size ()+"\n " +
190- "Requests: " +rc +"\n " +
191- "Requests/sec: " +(long )((rc -lastRequestCount )/(((double )(now -lastStatsTime ))/1000 ))+"\n " +
192- "Handler Exceptions: " +handleExceptionCount .get ()+"\n " +
193- "Socket Exceptions: " +socketExceptionCount .get ()+"\n " +
194- "Mac Connections Exceeded: " +maxConnectionsExceededCount .get ()+"\n " +
195- "Idle Closes: " +idleCloseCount .get ()+"\n " +
196- "Reply Errors: " +replyErrorCount .get ()+"\n "
165+ stats .stats ()+
166+ http2Stats .stats ()
197167 ).getBytes ();
198168
199- lastStatsTime = now ;
200- lastRequestCount = rc ;
201-
202169 exchange .sendResponseHeaders (200 ,output .length );
203170 exchange .getResponseBody ().write (output );
204171 exchange .getResponseBody ().close ();
205172 }
206173 }
207174
175+ /** log state to assist debugging */
176+ private class DebugHandler implements HttpHandler {
177+ @ Override
178+ public void handle (HttpExchange exchange ) throws IOException {
179+ logger .log (Level .INFO ,"logging debug state, requestor " +exchange .getRemoteAddress ());
180+ for (var connection : allConnections ) {
181+ connection .debug ();
182+ }
183+ Http2Exchange .debug ();
184+ exchange .sendResponseHeaders (200 ,-1 );
185+ }
186+ }
187+
188+
208189 public void bind (InetSocketAddress addr , int backlog ) throws IOException {
209190 if (bound ) {
210191 throw new BindException ("HttpServer already bound" );
@@ -365,12 +346,12 @@ public void run() {
365346 if (logger .isLoggable (Level .TRACE )) {
366347 logger .log (Level .TRACE , "accepted connection: " + s .toString ());
367348 }
368- connectionCount .incrementAndGet ();
349+ stats . connectionCount .incrementAndGet ();
369350 if (MAX_CONNECTIONS > 0 && allConnections .size () >= MAX_CONNECTIONS ) {
370351 // we've hit max limit of current open connections, so we go
371352 // ahead and close this connection without processing it
372353 try {
373- maxConnectionsExceededCount .incrementAndGet ();
354+ stats . maxConnectionsExceededCount .incrementAndGet ();
374355 logger .log (Level .WARNING , "closing accepted connection due to too many connections" );
375356 s .close ();
376357 } catch (IOException ignore ) {
@@ -429,7 +410,7 @@ public void run() {
429410
430411 } catch (Exception e ) {
431412 logger .log (Level .TRACE , "Dispatcher Exception" , e );
432- handleExceptionCount .incrementAndGet ();
413+ stats . handleExceptionCount .incrementAndGet ();
433414 closeConnection (c );
434415 }
435416 } catch (IOException e ) {
@@ -473,12 +454,23 @@ class Http2Exchange implements Runnable,HTTP2Connection.StreamHandler {
473454 final String protocol ;
474455
475456 private static final Set <Http2Exchange > allHttp2Exchanges = Collections .newSetFromMap (new ConcurrentHashMap <>());
457+ static void debug () {
458+ for (var exchange : allHttp2Exchanges ) {
459+ exchange .http2 .debug ();
460+ }
461+ }
476462
477463 Http2Exchange (String protocol , HttpConnection conn ) throws IOException {
478464 this .connection = conn ;
479465 this .protocol = protocol ;
480466
481- http2 = new HTTP2Connection (conn , connection .getInputStream (), connection .getOutputStream (), this );
467+ if (protocol .equals ("https2" )) {
468+ http2Stats .sslConnections .incrementAndGet ();
469+ } else {
470+ http2Stats .nonsslConnections .incrementAndGet ();
471+ }
472+
473+ http2 = new HTTP2Connection (conn ,http2Stats ,connection .getInputStream (), connection .getOutputStream (), this );
482474 }
483475
484476 static TimerTask createTask () {
@@ -512,7 +504,8 @@ public void run() {
512504 http2 .handle ();
513505 } catch (HTTP2Exception ex ) {
514506 logger .log (Level .WARNING , "ServerImpl http2 protocol exception " +http2 ,ex );
515- } catch (EOFException | SocketException ex ) {
507+ } catch (IOException ex ) {
508+ stats .socketExceptionCount .incrementAndGet ();
516509 logger .log (Level .DEBUG , "end of stream " +http2 );
517510 } catch (Exception ex ) {
518511 logger .log (Level .WARNING , "ServerImpl unexpected exception handling http2 connection " +http2 , ex );
@@ -536,7 +529,9 @@ public Executor getExecutor() {
536529 @ Override
537530 public void handleStream (HTTP2Stream stream ,InputStream in , OutputStream out ) throws IOException {
538531 connection .requestCount .incrementAndGet ();
539- requestCount .incrementAndGet ();
532+ stats .requestCount .incrementAndGet ();
533+
534+ http2Stats .totalStreams .incrementAndGet ();
540535
541536 var request = stream .getRequestHeaders ();
542537 var response = stream .getResponseHeaders ();
@@ -584,18 +579,24 @@ public void handleStream(HTTP2Stream stream,InputStream in, OutputStream out) th
584579 return ;
585580 }
586581
587- logger .log (Level .DEBUG ,() -> "http2 request on " +connection +" " +method +" for " +uri );
582+ logger .log (Level .TRACE ,() -> "http2 request on " +connection +" " +method +" for " +uri );
588583
589584 final List <Filter > sf = ctx .getSystemFilters ();
590585 final List <Filter > uf = ctx .getFilters ();
591586
592587 final Filter .Chain sc = new Filter .Chain (sf , ctx .getHandler ());
593588 final Filter .Chain uc = new Filter .Chain (uf , new LinkHandler (sc ));
594589
595- if (https ) {
596- uc .doFilter (new Http2ExchangeImpl (stream ,uri ,method ,ctx ,request ,response ,in ,out ));
597- } else {
598- uc .doFilter (new Http2ExchangeImpl (stream ,uri ,method ,ctx ,request ,response ,in ,out ));
590+ try {
591+ if (https ) {
592+ uc .doFilter (new Http2ExchangeImpl (stream ,uri ,method ,ctx ,request ,response ,in ,out ));
593+ } else {
594+ uc .doFilter (new Http2ExchangeImpl (stream ,uri ,method ,ctx ,request ,response ,in ,out ));
595+ }
596+ } catch (IOException e ) {
597+ } catch (Exception e ) {
598+ logger .log (Level .WARNING , "Dispatcher Exception on " +stream , e );
599+ stats .handleExceptionCount .incrementAndGet ();
599600 }
600601 }
601602 }
@@ -630,7 +631,7 @@ public void run() {
630631 } catch (SocketException e ) {
631632 // these are common with clients breaking connections etc
632633 logger .log (Level .TRACE , "ServerImpl IOException" , e );
633- socketExceptionCount .incrementAndGet ();
634+ stats . socketExceptionCount .incrementAndGet ();
634635 closeConnection (connection );
635636 break ;
636637 } catch (Exception e ) {
@@ -675,14 +676,14 @@ private void runPerRequest() throws IOException {
675676
676677 connection .inRequest = true ;
677678
678- if (requestLine == null ) {
679+ if (requestLine == null || "" . equals ( requestLine ) ) {
679680 /* connection closed */
680681 logger .log (Level .DEBUG , "no request line: closing" );
681682 closeConnection (connection );
682683 return ;
683684 }
684685 connection .requestCount .incrementAndGet ();
685- requestCount .incrementAndGet ();
686+ stats . requestCount .incrementAndGet ();
686687
687688 logger .log (Level .DEBUG , () -> "Exchange request line: " + requestLine );
688689 int space = requestLine .indexOf (" " );
@@ -870,7 +871,7 @@ void sendReply(
870871 }
871872 } catch (IOException e ) {
872873 logger .log (Level .TRACE , "ServerImpl.sendReply" , e );
873- replyErrorCount .incrementAndGet ();
874+ stats . replyErrorCount .incrementAndGet ();
874875 closeConnection (connection );
875876 }
876877 }
@@ -915,7 +916,7 @@ public void run() {
915916 for (var c : allConnections ) {
916917 if (now - c .lastActivityTime >= IDLE_INTERVAL && !c .inRequest ) {
917918 logger .log (Level .DEBUG , "closing idle connection" );
918- idleCloseCount .incrementAndGet ();
919+ stats . idleCloseCount .incrementAndGet ();
919920 closeConnection (c );
920921 // idle.add(c);
921922 } else if (c .noActivity && (now - c .lastActivityTime >= NEWLY_ACCEPTED_CONN_IDLE_INTERVAL )) {
0 commit comments