4949import static okhttp3 .internal .Internal .initializeInstanceForTests ;
5050import static okhttp3 .internal .Util .EMPTY_BYTE_ARRAY ;
5151import static okhttp3 .internal .Util .EMPTY_HEADERS ;
52+ import static okhttp3 .internal .http2 .Http2Connection .AWAIT_PING ;
53+ import static okhttp3 .internal .http2 .Http2Connection .DEGRADED_PING ;
54+ import static okhttp3 .internal .http2 .Http2Connection .DEGRADED_PONG_TIMEOUT_NS ;
5255import static okhttp3 .internal .http2 .Http2Connection .Listener .REFUSE_INCOMING_STREAMS ;
5356import static okhttp3 .internal .http2 .Settings .DEFAULT_INITIAL_WINDOW_SIZE ;
5457import static okhttp3 .internal .http2 .Settings .ENABLE_PUSH ;
@@ -173,7 +176,7 @@ public final class Http2ConnectionTest {
173176 peer .acceptFrame (); // ACK
174177 peer .sendFrame ().windowUpdate (0 , 10 ); // Increase the connection window size.
175178 peer .acceptFrame (); // PING
176- peer .sendFrame ().ping (true , 1 , 0 );
179+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
177180 peer .acceptFrame (); // HEADERS STREAM 3
178181 peer .sendFrame ().windowUpdate (3 , 5 );
179182 peer .acceptFrame (); // DATA STREAM 3 "abcde"
@@ -244,7 +247,7 @@ public final class Http2ConnectionTest {
244247 peer .acceptFrame (); // SYN_STREAM 5
245248 peer .sendFrame ().goAway (3 , ErrorCode .PROTOCOL_ERROR , EMPTY_BYTE_ARRAY );
246249 peer .acceptFrame (); // PING
247- peer .sendFrame ().ping (true , 1 , 0 );
250+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
248251 peer .acceptFrame (); // DATA STREAM 3
249252 peer .play ();
250253
@@ -527,7 +530,7 @@ public final class Http2ConnectionTest {
527530 peer .sendFrame ().headers (false , 3 , headerEntries ("a" , "android" ));
528531 peer .sendFrame ().data (true , 3 , new Buffer ().writeUtf8 ("robot" ), 5 );
529532 peer .acceptFrame (); // PING
530- peer .sendFrame ().ping (true , 1 , 0 ); // PING
533+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PING
531534 peer .play ();
532535
533536 // play it back
@@ -559,7 +562,7 @@ public final class Http2ConnectionTest {
559562 peer .acceptFrame (); // SYN_STREAM
560563 peer .acceptFrame (); // PING
561564 peer .sendFrame ().headers (true , 3 , headerEntries ("headers" , "bam" ));
562- peer .sendFrame ().ping (true , 1 , 0 ); // PONG
565+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PONG
563566 peer .play ();
564567
565568 // play it back
@@ -587,7 +590,7 @@ public final class Http2ConnectionTest {
587590 peer .sendFrame ().headers (false , 3 , headerEntries ("headers" , "bam" ));
588591 peer .acceptFrame (); // PING
589592 peer .sendFrame ().headers (true , 3 , headerEntries ("trailers" , "boom" ));
590- peer .sendFrame ().ping (true , 1 , 0 ); // PONG
593+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PONG
591594 peer .play ();
592595
593596 // play it back
@@ -652,7 +655,7 @@ public final class Http2ConnectionTest {
652655 peer .sendFrame ().data (false , 3 , new Buffer ().writeUtf8 ("robot" ), 5 );
653656 peer .sendFrame ().headers (true , 3 , headerEntries ("trailers" , "boom" ));
654657 peer .acceptFrame (); // PING
655- peer .sendFrame ().ping (true , 1 , 0 ); // PONG
658+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PONG
656659 peer .play ();
657660
658661 // play it back
@@ -673,7 +676,7 @@ public final class Http2ConnectionTest {
673676 peer .acceptFrame (); // SYN_STREAM
674677 peer .sendFrame ().rstStream (3 , ErrorCode .PROTOCOL_ERROR );
675678 peer .acceptFrame (); // PING
676- peer .sendFrame ().ping (true , 1 , 0 ); // PONG
679+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PONG
677680 peer .play ();
678681
679682 // play it back
@@ -692,7 +695,7 @@ public final class Http2ConnectionTest {
692695 peer .sendFrame ().settings (new Settings ());
693696 peer .acceptFrame (); // ACK
694697 peer .acceptFrame (); // PING
695- peer .sendFrame ().ping (true , 1 , 0 );
698+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
696699 peer .play ();
697700
698701 // Play it back.
@@ -716,7 +719,7 @@ public final class Http2ConnectionTest {
716719 peer .sendFrame ().headers (false , 3 , headerEntries ("headers" , "bam" ));
717720 peer .sendFrame ().data (true , 3 , new Buffer ().writeUtf8 ("robot" ), 5 );
718721 peer .acceptFrame (); // PING
719- peer .sendFrame ().ping (true , 1 , 0 ); // PONG
722+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PONG
720723 peer .play ();
721724
722725 // play it back
@@ -747,7 +750,7 @@ public final class Http2ConnectionTest {
747750 peer .acceptFrame (); // HEADERS
748751 peer .sendFrame ().headers (true , 3 , headerEntries ("a" , "android" ));
749752 peer .acceptFrame (); // PING
750- peer .sendFrame ().ping (true , 1 , 0 ); // PING
753+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PING
751754 peer .play ();
752755
753756 // play it back
@@ -781,7 +784,7 @@ public final class Http2ConnectionTest {
781784 peer .acceptFrame (); // SYN_STREAM
782785 peer .acceptFrame (); // PING
783786 peer .sendFrame ().headers (true , 3 , headerEntries ("a" , "android" ));
784- peer .sendFrame ().ping (true , 1 , 0 );
787+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
785788 peer .play ();
786789
787790 // play it back
@@ -822,7 +825,7 @@ public final class Http2ConnectionTest {
822825 peer .sendFrame ().settings (new Settings ());
823826 peer .acceptFrame (); // ACK
824827 peer .acceptFrame (); // PING
825- peer .sendFrame ().ping (true , 1 , 5 );
828+ peer .sendFrame ().ping (true , AWAIT_PING , 5 );
826829 peer .play ();
827830
828831 // play it back
@@ -837,10 +840,8 @@ public final class Http2ConnectionTest {
837840 InFrame pingFrame = peer .takeFrame ();
838841 assertThat (pingFrame .type ).isEqualTo (Http2 .TYPE_PING );
839842 assertThat (pingFrame .streamId ).isEqualTo (0 );
840- // OkOk
841- assertThat (pingFrame .payload1 ).isEqualTo (0x4f4b6f6b );
842- // donut
843- assertThat (pingFrame .payload2 ).isEqualTo (0xf09f8da9 );
843+ assertThat (pingFrame .payload1 ).isEqualTo (AWAIT_PING );
844+ assertThat (pingFrame .payload2 ).isEqualTo (0x4f4b6f6b ); // OKok.
844845 assertThat (pingFrame .ack ).isFalse ();
845846 }
846847
@@ -850,7 +851,7 @@ public final class Http2ConnectionTest {
850851 peer .acceptFrame (); // ACK
851852 peer .sendFrame ().ping (false , 2 , 0 );
852853 peer .acceptFrame (); // PING
853- peer .sendFrame ().ping (true , 3 , 0 ); // This ping will not be returned .
854+ peer .sendFrame ().ping (true , 99 , 0 ); // This pong is silently ignored .
854855 peer .sendFrame ().ping (false , 4 , 0 );
855856 peer .acceptFrame (); // PING
856857 peer .play ();
@@ -1003,7 +1004,7 @@ public final class Http2ConnectionTest {
10031004 peer .acceptFrame (); // SYN_STREAM
10041005 peer .sendFrame ().rstStream (3 , ErrorCode .CANCEL );
10051006 peer .acceptFrame (); // PING
1006- peer .sendFrame ().ping (true , 1 , 0 );
1007+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
10071008 peer .play ();
10081009
10091010 // play it back
@@ -1132,7 +1133,7 @@ public final class Http2ConnectionTest {
11321133 peer .sendFrame ().headers (false , 3 , headerEntries ("b" , "banana" ));
11331134 peer .sendFrame ().data (true , 3 , new Buffer ().writeUtf8 ("square" ), 6 );
11341135 peer .acceptFrame (); // PING
1135- peer .sendFrame ().ping (true , 1 , 0 );
1136+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
11361137 peer .play ();
11371138
11381139 // play it back
@@ -1158,7 +1159,7 @@ public final class Http2ConnectionTest {
11581159 peer .sendFrame ().headers (false , 3 , headerEntries ("a" , "android" ));
11591160 peer .acceptFrame (); // PING
11601161 peer .sendFrame ().headers (false , 3 , headerEntries ("b" , "banana" ));
1161- peer .sendFrame ().ping (true , 1 , 0 );
1162+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
11621163 peer .play ();
11631164
11641165 // play it back
@@ -1270,7 +1271,7 @@ public final class Http2ConnectionTest {
12701271 peer .acceptFrame (); // SYN_STREAM 3
12711272 peer .acceptFrame (); // PING.
12721273 peer .sendFrame ().goAway (3 , ErrorCode .PROTOCOL_ERROR , Util .EMPTY_BYTE_ARRAY );
1273- peer .sendFrame ().ping (true , 1 , 0 );
1274+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
12741275 peer .acceptFrame (); // DATA STREAM 1
12751276 peer .play ();
12761277
@@ -1321,18 +1322,18 @@ public final class Http2ConnectionTest {
13211322 peer .acceptFrame (); // GOAWAY
13221323 peer .acceptFrame (); // PING
13231324 peer .sendFrame ().headers (false , 2 , headerEntries ("b" , "b" )); // Should be ignored!
1324- peer .sendFrame ().ping (true , 1 , 0 );
1325+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
13251326 peer .play ();
13261327
13271328 // play it back
13281329 Http2Connection connection = connect (peer );
13291330 connection .newStream (headerEntries ("a" , "android" ), false );
13301331 synchronized (connection ) {
1331- if (connection .shutdown ) {
1332+ if (! connection .isHealthy ( System . nanoTime ()) ) {
13321333 throw new ConnectionShutdownException ();
13331334 }
13341335 }
1335- connection .writePing (false , 0x01 , 0x02 );
1336+ connection .writePing ();
13361337 connection .shutdown (ErrorCode .PROTOCOL_ERROR );
13371338 assertThat (connection .openStreamCount ()).isEqualTo (1 );
13381339 connection .awaitPong (); // Prevent the peer from exiting prematurely.
@@ -1422,23 +1423,34 @@ public final class Http2ConnectionTest {
14221423 assertThat (peer .takeFrame ().type ).isEqualTo (Http2 .TYPE_RST_STREAM );
14231424 }
14241425
1426+ /**
1427+ * Confirm that the client times out if the server stalls after 3 bytes. After the timeout the
1428+ * connection is still considered healthy while we await the degraded pong. When that doesn't
1429+ * arrive the connection goes unhealthy.
1430+ */
14251431 @ Test public void readTimesOut () throws Exception {
14261432 // write the mocking script
14271433 peer .sendFrame ().settings (new Settings ());
14281434 peer .acceptFrame (); // ACK
14291435 peer .acceptFrame (); // SYN_STREAM
14301436 peer .sendFrame ().headers (false , 3 , headerEntries ("a" , "android" ));
1437+ peer .sendFrame ().data (false , 3 , new Buffer ().writeUtf8 ("abc" ), 3 );
14311438 peer .acceptFrame (); // RST_STREAM
1439+ peer .acceptFrame (); // DEGRADED PING
1440+ peer .acceptFrame (); // AWAIT PING
1441+ peer .sendFrame ().ping (true , DEGRADED_PING , 1 ); // DEGRADED PONG
1442+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // AWAIT PONG
14321443 peer .play ();
14331444
14341445 // play it back
14351446 Http2Connection connection = connect (peer );
14361447 Http2Stream stream = connection .newStream (headerEntries ("b" , "banana" ), false );
14371448 stream .readTimeout ().timeout (500 , TimeUnit .MILLISECONDS );
1438- Source source = stream .getSource ();
1449+ BufferedSource source = Okio .buffer (stream .getSource ());
1450+ source .require (3 );
14391451 long startNanos = System .nanoTime ();
14401452 try {
1441- source .read ( new Buffer (), 1 );
1453+ source .require ( 4 );
14421454 fail ();
14431455 } catch (InterruptedIOException expected ) {
14441456 }
@@ -1448,9 +1460,22 @@ public final class Http2ConnectionTest {
14481460 assertThat ((double ) TimeUnit .NANOSECONDS .toMillis (elapsedNanos )).isCloseTo (500d , offset (200d ));
14491461 assertThat (connection .openStreamCount ()).isEqualTo (0 );
14501462
1463+ // When the timeout is sent the connection doesn't immediately go unhealthy.
1464+ assertThat (connection .isHealthy (System .nanoTime ())).isTrue ();
1465+
1466+ // But if the ping doesn't arrive, the connection goes unhealthy.
1467+ Thread .sleep (TimeUnit .NANOSECONDS .toMillis (DEGRADED_PONG_TIMEOUT_NS ));
1468+ assertThat (connection .isHealthy (System .nanoTime ())).isFalse ();
1469+
1470+ // When a pong does arrive, the connection becomes healthy again.
1471+ connection .writePingAndAwaitPong ();
1472+ assertThat (connection .isHealthy (System .nanoTime ())).isTrue ();
1473+
14511474 // verify the peer received what was expected
14521475 assertThat (peer .takeFrame ().type ).isEqualTo (Http2 .TYPE_HEADERS );
14531476 assertThat (peer .takeFrame ().type ).isEqualTo (Http2 .TYPE_RST_STREAM );
1477+ assertThat (peer .takeFrame ().type ).isEqualTo (Http2 .TYPE_PING );
1478+ assertThat (peer .takeFrame ().type ).isEqualTo (Http2 .TYPE_PING );
14541479 }
14551480
14561481 @ Test public void writeTimesOutAwaitingStreamWindow () throws Exception {
@@ -1461,7 +1486,7 @@ public final class Http2ConnectionTest {
14611486 peer .sendFrame ().settings (peerSettings );
14621487 peer .acceptFrame (); // ACK SETTINGS
14631488 peer .acceptFrame (); // PING
1464- peer .sendFrame ().ping (true , 1 , 0 );
1489+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
14651490 peer .acceptFrame (); // SYN_STREAM
14661491 peer .sendFrame ().headers (false , 3 , headerEntries ("a" , "android" ));
14671492 peer .acceptFrame (); // DATA
@@ -1504,11 +1529,11 @@ public final class Http2ConnectionTest {
15041529 peer .sendFrame ().settings (peerSettings );
15051530 peer .acceptFrame (); // ACK SETTINGS
15061531 peer .acceptFrame (); // PING
1507- peer .sendFrame ().ping (true , 1 , 0 );
1532+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
15081533 peer .acceptFrame (); // SYN_STREAM
15091534 peer .sendFrame ().headers (false , 3 , headerEntries ("a" , "android" ));
15101535 peer .acceptFrame (); // PING
1511- peer .sendFrame ().ping (true , 3 , 0 );
1536+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
15121537 peer .acceptFrame (); // DATA
15131538 peer .acceptFrame (); // RST_STREAM
15141539 peer .play ();
@@ -1576,7 +1601,7 @@ public final class Http2ConnectionTest {
15761601 peer .acceptFrame (); // PING
15771602 peer .sendFrame ().headers (false , 3 , headerEntries ("a" , "android" ));
15781603 peer .sendFrame ().headers (false , 3 , headerEntries ("c" , "c3po" ));
1579- peer .sendFrame ().ping (true , 1 , 0 );
1604+ peer .sendFrame ().ping (true , AWAIT_PING , 0 );
15801605 peer .play ();
15811606
15821607 // play it back
@@ -1601,7 +1626,7 @@ public final class Http2ConnectionTest {
16011626 peer .sendFrame ().headers (false , 3 , headerEntries ("a" , "android" ));
16021627 peer .acceptFrame (); // PING
16031628 peer .sendFrame ().headers (true , 3 , headerEntries ("c" , "cola" ));
1604- peer .sendFrame ().ping (true , 1 , 0 ); // PONG
1629+ peer .sendFrame ().ping (true , AWAIT_PING , 0 ); // PONG
16051630 peer .play ();
16061631
16071632 // play it back
0 commit comments