| /* |
| * |
| * Copyright 2016 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| // Binary http2 is used to test http2 error edge cases like GOAWAYs and |
| // RST_STREAMs |
| // |
| // Documentation: |
| // https://github.com/grpc/grpc/blob/master/doc/negative-http2-interop-test-descriptions.md |
| package main |
| |
| import ( |
| "context" |
| "flag" |
| "net" |
| "strconv" |
| "sync" |
| "time" |
| |
| "google.golang.org/grpc" |
| "google.golang.org/grpc/codes" |
| "google.golang.org/grpc/grpclog" |
| "google.golang.org/grpc/interop" |
| testpb "google.golang.org/grpc/interop/grpc_testing" |
| "google.golang.org/grpc/status" |
| ) |
| |
| var ( |
| serverHost = flag.String("server_host", "localhost", "The server host name") |
| serverPort = flag.Int("server_port", 8080, "The server port number") |
| testCase = flag.String("test_case", "goaway", |
| `Configure different test cases. Valid options are: |
| goaway : client sends two requests, the server will send a goaway in between; |
| rst_after_header : server will send rst_stream after it sends headers; |
| rst_during_data : server will send rst_stream while sending data; |
| rst_after_data : server will send rst_stream after sending data; |
| ping : server will send pings between each http2 frame; |
| max_streams : server will ensure that the max_concurrent_streams limit is upheld;`) |
| largeReqSize = 271828 |
| largeRespSize = 314159 |
| |
| logger = grpclog.Component("interop") |
| ) |
| |
| func largeSimpleRequest() *testpb.SimpleRequest { |
| pl := interop.ClientNewPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize) |
| return &testpb.SimpleRequest{ |
| ResponseType: testpb.PayloadType_COMPRESSABLE, |
| ResponseSize: int32(largeRespSize), |
| Payload: pl, |
| } |
| } |
| |
| // sends two unary calls. The server asserts that the calls use different connections. |
| func goaway(tc testpb.TestServiceClient) { |
| interop.DoLargeUnaryCall(tc) |
| // sleep to ensure that the client has time to recv the GOAWAY. |
| // TODO(ncteisen): make this less hacky. |
| time.Sleep(1 * time.Second) |
| interop.DoLargeUnaryCall(tc) |
| } |
| |
| func rstAfterHeader(tc testpb.TestServiceClient) { |
| req := largeSimpleRequest() |
| reply, err := tc.UnaryCall(context.Background(), req) |
| if reply != nil { |
| logger.Fatalf("Client received reply despite server sending rst stream after header") |
| } |
| if status.Code(err) != codes.Internal { |
| logger.Fatalf("%v.UnaryCall() = _, %v, want _, %v", tc, status.Code(err), codes.Internal) |
| } |
| } |
| |
| func rstDuringData(tc testpb.TestServiceClient) { |
| req := largeSimpleRequest() |
| reply, err := tc.UnaryCall(context.Background(), req) |
| if reply != nil { |
| logger.Fatalf("Client received reply despite server sending rst stream during data") |
| } |
| if status.Code(err) != codes.Unknown { |
| logger.Fatalf("%v.UnaryCall() = _, %v, want _, %v", tc, status.Code(err), codes.Unknown) |
| } |
| } |
| |
| func rstAfterData(tc testpb.TestServiceClient) { |
| req := largeSimpleRequest() |
| reply, err := tc.UnaryCall(context.Background(), req) |
| if reply != nil { |
| logger.Fatalf("Client received reply despite server sending rst stream after data") |
| } |
| if status.Code(err) != codes.Internal { |
| logger.Fatalf("%v.UnaryCall() = _, %v, want _, %v", tc, status.Code(err), codes.Internal) |
| } |
| } |
| |
| func ping(tc testpb.TestServiceClient) { |
| // The server will assert that every ping it sends was ACK-ed by the client. |
| interop.DoLargeUnaryCall(tc) |
| } |
| |
| func maxStreams(tc testpb.TestServiceClient) { |
| interop.DoLargeUnaryCall(tc) |
| var wg sync.WaitGroup |
| for i := 0; i < 15; i++ { |
| wg.Add(1) |
| go func() { |
| defer wg.Done() |
| interop.DoLargeUnaryCall(tc) |
| }() |
| } |
| wg.Wait() |
| } |
| |
| func main() { |
| flag.Parse() |
| serverAddr := net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort)) |
| var opts []grpc.DialOption |
| opts = append(opts, grpc.WithInsecure()) |
| conn, err := grpc.Dial(serverAddr, opts...) |
| if err != nil { |
| logger.Fatalf("Fail to dial: %v", err) |
| } |
| defer conn.Close() |
| tc := testpb.NewTestServiceClient(conn) |
| switch *testCase { |
| case "goaway": |
| goaway(tc) |
| logger.Infoln("goaway done") |
| case "rst_after_header": |
| rstAfterHeader(tc) |
| logger.Infoln("rst_after_header done") |
| case "rst_during_data": |
| rstDuringData(tc) |
| logger.Infoln("rst_during_data done") |
| case "rst_after_data": |
| rstAfterData(tc) |
| logger.Infoln("rst_after_data done") |
| case "ping": |
| ping(tc) |
| logger.Infoln("ping done") |
| case "max_streams": |
| maxStreams(tc) |
| logger.Infoln("max_streams done") |
| default: |
| logger.Fatal("Unsupported test case: ", *testCase) |
| } |
| } |