|
63 | 63 | import io.grpc.internal.ClientStreamListener.RpcProgress; |
64 | 64 | import io.grpc.internal.ClientTransport; |
65 | 65 | import io.grpc.internal.ClientTransport.PingCallback; |
| 66 | +import io.grpc.internal.DisconnectError; |
| 67 | +import io.grpc.internal.GoAwayDisconnectError; |
66 | 68 | import io.grpc.internal.GrpcAttributes; |
67 | 69 | import io.grpc.internal.GrpcUtil; |
68 | 70 | import io.grpc.internal.KeepAliveManager; |
@@ -587,6 +589,55 @@ public void receivedGoAwayShouldFailNewStreams() throws Exception { |
587 | 589 | status.getDescription()); |
588 | 590 | } |
589 | 591 |
|
| 592 | + @Test |
| 593 | + public void receivedGoAway_unrecognizedErrorCode_shouldFailNewStreamsAndReportMetric() |
| 594 | + throws Exception { |
| 595 | + // Read a GOAWAY with an unrecognized error code (e.g. Apache httpd error code 70007). |
| 596 | + channelRead(goAwayFrame(0, 70007, Unpooled.copiedBuffer("apache error", UTF_8))); |
| 597 | + |
| 598 | + ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class); |
| 599 | + ArgumentCaptor<DisconnectError> disconnectErrorCaptor = |
| 600 | + ArgumentCaptor.forClass(DisconnectError.class); |
| 601 | + verify(listener).transportShutdown(statusCaptor.capture(), disconnectErrorCaptor.capture()); |
| 602 | + |
| 603 | + assertEquals(Status.UNAVAILABLE.getCode(), statusCaptor.getValue().getCode()); |
| 604 | + assertEquals( |
| 605 | + "GOAWAY shut down transport. Unrecognized HTTP/2 error code: 70007, " |
| 606 | + + "debug data: apache error", |
| 607 | + statusCaptor.getValue().getDescription()); |
| 608 | + |
| 609 | + DisconnectError disconnectError = disconnectErrorCaptor.getValue(); |
| 610 | + assertEquals( |
| 611 | + new GoAwayDisconnectError(GrpcUtil.Http2Error.INTERNAL_ERROR), disconnectError); |
| 612 | + |
| 613 | + // Creating new stream must fail immediately with UNAVAILABLE |
| 614 | + ChannelFuture future = enqueue(newCreateStreamCommand(grpcHeaders, streamTransportState)); |
| 615 | + assertTrue(future.isDone()); |
| 616 | + assertFalse(future.isSuccess()); |
| 617 | + Status status = Status.fromThrowable(future.cause()); |
| 618 | + assertEquals(Status.UNAVAILABLE.getCode(), status.getCode()); |
| 619 | + assertEquals( |
| 620 | + "GOAWAY shut down transport. Unrecognized HTTP/2 error code: 70007, " |
| 621 | + + "debug data: apache error", |
| 622 | + status.getDescription()); |
| 623 | + } |
| 624 | + |
| 625 | + @Test |
| 626 | + public void receivedGoAway_unrecognizedErrorCode_shouldCloseActiveStreams() throws Exception { |
| 627 | + createStream(); |
| 628 | + |
| 629 | + // Read a GOAWAY indicating our stream (id 3) was not processed. |
| 630 | + channelRead(goAwayFrame(0, 70007, Unpooled.copiedBuffer("apache error", UTF_8))); |
| 631 | + |
| 632 | + ArgumentCaptor<Status> captor = ArgumentCaptor.forClass(Status.class); |
| 633 | + verify(streamListener).closed(captor.capture(), eq(PROCESSED), ArgumentMatchers.notNull()); |
| 634 | + assertEquals(Status.INTERNAL.getCode(), captor.getValue().getCode()); |
| 635 | + assertEquals( |
| 636 | + "Abrupt GOAWAY closed sent stream. Unrecognized HTTP/2 error code: 70007, " |
| 637 | + + "debug data: apache error", |
| 638 | + captor.getValue().getDescription()); |
| 639 | + } |
| 640 | + |
590 | 641 | // This test is not as useful as it looks, because the HTTP/2 Netty code catches and doesn't |
591 | 642 | // propagate exceptions during the onGoAwayReceived callback. |
592 | 643 | @Test |
|
0 commit comments