7575import org .apache .hc .core5 .http2 .H2ConnectionException ;
7676import org .apache .hc .core5 .http2 .H2Error ;
7777import org .apache .hc .core5 .http2 .H2StreamResetException ;
78+ import org .apache .hc .core5 .http2 .H2StreamTimeoutException ;
7879import org .apache .hc .core5 .http2 .config .H2Config ;
7980import org .apache .hc .core5 .http2 .config .H2Param ;
8081import org .apache .hc .core5 .http2 .config .H2Setting ;
@@ -439,6 +440,10 @@ public final void onInput(final ByteBuffer src) throws HttpException, IOExceptio
439440 for (;;) {
440441 final RawFrame frame = inputBuffer .read (src , ioSession );
441442 if (frame != null ) {
443+ if (connState .compareTo (ConnectionHandshake .SHUTDOWN ) < 0 ) {
444+ checkStreamTimeouts (System .nanoTime ());
445+ }
446+
442447 if (streamListener != null ) {
443448 streamListener .onFrameInput (this , frame .getStreamId (), frame );
444449 }
@@ -455,6 +460,9 @@ public final void onInput(final ByteBuffer src) throws HttpException, IOExceptio
455460 break ;
456461 }
457462 }
463+ if (connState .compareTo (ConnectionHandshake .SHUTDOWN ) < 0 ) {
464+ checkStreamTimeouts (System .nanoTime ());
465+ }
458466 }
459467 }
460468
@@ -532,6 +540,11 @@ public final void onOutput() throws HttpException, IOException {
532540 }
533541 }
534542 }
543+
544+ if (connState .compareTo (ConnectionHandshake .SHUTDOWN ) < 0 ) {
545+ checkStreamTimeouts (System .nanoTime ());
546+ }
547+
535548 if (connState .compareTo (ConnectionHandshake .GRACEFUL_SHUTDOWN ) == 0 ) {
536549 int liveStreams = 0 ;
537550 for (final Iterator <H2Stream > it = streams .iterator (); it .hasNext (); ) {
@@ -643,6 +656,7 @@ private void executeRequest(final RequestExecutionCommand requestExecutionComman
643656 requestExecutionCommand .getExchangeHandler (),
644657 requestExecutionCommand .getPushHandlerFactory (),
645658 requestExecutionCommand .getContext ()));
659+ initializeStreamTimeouts (stream );
646660
647661 if (streamListener != null ) {
648662 final int initInputWindow = stream .getInputWindow ().get ();
@@ -765,10 +779,12 @@ private void consumeFrame(final RawFrame frame) throws HttpException, IOExceptio
765779 final H2StreamChannel channel = createChannel (streamId );
766780 if (connState .compareTo (ConnectionHandshake .ACTIVE ) <= 0 ) {
767781 stream = streams .createActive (channel , incomingRequest (channel ));
782+ initializeStreamTimeouts (stream );
768783 streams .resetIfExceedsMaxConcurrentLimit (stream , localConfig .getMaxConcurrentStreams ());
769784 } else {
770785 channel .localReset (H2Error .REFUSED_STREAM );
771786 stream = streams .createActive (channel , NoopH2StreamHandler .INSTANCE );
787+ initializeStreamTimeouts (stream );
772788 }
773789 } else if (stream .isLocalClosed () && stream .isRemoteClosed ()) {
774790 throw new H2ConnectionException (H2Error .STREAM_CLOSED , "Stream closed" );
@@ -959,6 +975,7 @@ private void consumeFrame(final RawFrame frame) throws HttpException, IOExceptio
959975 channel .localReset (H2Error .REFUSED_STREAM );
960976 promisedStream = streams .createActive (channel , NoopH2StreamHandler .INSTANCE );
961977 }
978+ initializeStreamTimeouts (promisedStream );
962979 try {
963980 consumePushPromiseFrame (frame , payload , promisedStream );
964981 } catch (final H2StreamResetException ex ) {
@@ -1364,8 +1381,18 @@ H2StreamChannel createChannel(final int streamId) {
13641381 return new H2StreamChannelImpl (streamId , initInputWinSize , initOutputWinSize );
13651382 }
13661383
1367- H2Stream createStream (final H2StreamChannel channel , final H2StreamHandler streamHandler ) throws H2ConnectionException {
1368- return streams .createActive (channel , streamHandler );
1384+ private void initializeStreamTimeouts (final H2Stream stream ) {
1385+ final Timeout socketTimeout = ioSession .getSocketTimeout ();
1386+ if (socketTimeout != null && socketTimeout .isEnabled ()) {
1387+ stream .setIdleTimeout (socketTimeout );
1388+ stream .setLifetimeTimeout (socketTimeout );
1389+ }
1390+ }
1391+
1392+ H2Stream createStream (final H2StreamChannel channel , final H2StreamHandler streamHandler ) {
1393+ final H2Stream stream = streams .createActive (channel , streamHandler );
1394+ initializeStreamTimeouts (stream );
1395+ return stream ;
13691396 }
13701397
13711398 private void recordPriorityFromHeaders (final int streamId , final List <? extends Header > headers ) {
@@ -1468,6 +1495,7 @@ public void push(final List<Header> headers, final AsyncPushProducer pushProduce
14681495 final int promisedStreamId = streams .generateStreamId ();
14691496 final H2StreamChannel channel = createChannel (promisedStreamId );
14701497 final H2Stream stream = streams .createReserved (channel , outgoingPushPromise (channel , pushProducer ));
1498+ initializeStreamTimeouts (stream );
14711499
14721500 commitPushPromise (id , promisedStreamId , headers );
14731501 stream .markRemoteClosed ();
@@ -1584,4 +1612,51 @@ public String toString() {
15841612
15851613 }
15861614
1615+ private void checkStreamTimeouts (final long nowNanos ) throws IOException {
1616+ for (final Iterator <H2Stream > it = streams .iterator (); it .hasNext (); ) {
1617+ final H2Stream stream = it .next ();
1618+ if (!stream .isActive ()) {
1619+ continue ;
1620+ }
1621+
1622+ final Timeout idleTimeout = stream .getIdleTimeout ();
1623+ final Timeout lifetimeTimeout = stream .getLifetimeTimeout ();
1624+ if ((idleTimeout == null || !idleTimeout .isEnabled ())
1625+ && (lifetimeTimeout == null || !lifetimeTimeout .isEnabled ())) {
1626+ continue ;
1627+ }
1628+
1629+ final long created = stream .getCreatedNanos ();
1630+ final long last = stream .getLastActivityNanos ();
1631+
1632+ if (idleTimeout != null && idleTimeout .isEnabled ()) {
1633+ final long idleNanos = idleTimeout .toNanoseconds ();
1634+ if (idleNanos > 0 && nowNanos - last > idleNanos ) {
1635+ final int streamId = stream .getId ();
1636+ final H2StreamTimeoutException ex = new H2StreamTimeoutException (
1637+ "HTTP/2 stream idle timeout (" + idleTimeout + ")" ,
1638+ streamId ,
1639+ idleTimeout ,
1640+ true );
1641+ stream .localReset (ex , H2Error .CANCEL );
1642+ // Once reset due to idle timeout, we do not care about lifetime anymore
1643+ continue ;
1644+ }
1645+ }
1646+
1647+ if (lifetimeTimeout != null && lifetimeTimeout .isEnabled ()) {
1648+ final long lifeNanos = lifetimeTimeout .toNanoseconds ();
1649+ if (lifeNanos > 0 && nowNanos - created > lifeNanos ) {
1650+ final int streamId = stream .getId ();
1651+ final H2StreamTimeoutException ex = new H2StreamTimeoutException (
1652+ "HTTP/2 stream lifetime timeout (" + lifetimeTimeout + ")" ,
1653+ streamId ,
1654+ lifetimeTimeout ,
1655+ false );
1656+ stream .localReset (ex , H2Error .CANCEL );
1657+ }
1658+ }
1659+ }
1660+ }
1661+
15871662}
0 commit comments