...

Source file src/google.golang.org/grpc/test/http_header_end2end_test.go

Documentation: google.golang.org/grpc/test

     1  /*
     2  *
     3  * Copyright 2022 gRPC authors.
     4  *
     5  * Licensed under the Apache License, Version 2.0 (the "License");
     6  * you may not use this file except in compliance with the License.
     7  * You may obtain a copy of the License at
     8  *
     9  *     http://www.apache.org/licenses/LICENSE-2.0
    10  *
    11  * Unless required by applicable law or agreed to in writing, software
    12  * distributed under the License is distributed on an "AS IS" BASIS,
    13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  * See the License for the specific language governing permissions and
    15  * limitations under the License.
    16  *
    17   */
    18  package test
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"net"
    24  	"testing"
    25  
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/credentials/insecure"
    29  	"google.golang.org/grpc/internal/transport"
    30  	"google.golang.org/grpc/status"
    31  
    32  	testgrpc "google.golang.org/grpc/interop/grpc_testing"
    33  )
    34  
    35  func (s) TestHTTPHeaderFrameErrorHandlingHTTPMode(t *testing.T) {
    36  	type test struct {
    37  		name    string
    38  		header  []string
    39  		errCode codes.Code
    40  	}
    41  
    42  	var tests []test
    43  
    44  	// Non-gRPC content-type fallback path.
    45  	for httpCode := range transport.HTTPStatusConvTab {
    46  		tests = append(tests, test{
    47  			name: fmt.Sprintf("Non-gRPC content-type fallback path with httpCode: %v", httpCode),
    48  			header: []string{
    49  				":status", fmt.Sprintf("%d", httpCode),
    50  				"content-type", "text/html", // non-gRPC content type to switch to HTTP mode.
    51  				"grpc-status", "1", // Make up a gRPC status error
    52  				"grpc-status-details-bin", "???", // Make up a gRPC field parsing error
    53  			},
    54  			errCode: transport.HTTPStatusConvTab[int(httpCode)],
    55  		})
    56  	}
    57  
    58  	// Missing content-type fallback path.
    59  	for httpCode := range transport.HTTPStatusConvTab {
    60  		tests = append(tests, test{
    61  			name: fmt.Sprintf("Missing content-type fallback path with httpCode: %v", httpCode),
    62  			header: []string{
    63  				":status", fmt.Sprintf("%d", httpCode),
    64  				// Omitting content type to switch to HTTP mode.
    65  				"grpc-status", "1", // Make up a gRPC status error
    66  				"grpc-status-details-bin", "???", // Make up a gRPC field parsing error
    67  			},
    68  			errCode: transport.HTTPStatusConvTab[int(httpCode)],
    69  		})
    70  	}
    71  
    72  	// Malformed HTTP status when fallback.
    73  	tests = append(tests, test{
    74  		name: "Malformed HTTP status when fallback",
    75  		header: []string{
    76  			":status", "abc",
    77  			// Omitting content type to switch to HTTP mode.
    78  			"grpc-status", "1", // Make up a gRPC status error
    79  			"grpc-status-details-bin", "???", // Make up a gRPC field parsing error
    80  		},
    81  		errCode: codes.Internal,
    82  	})
    83  
    84  	for _, test := range tests {
    85  		t.Run(test.name, func(t *testing.T) {
    86  			serverAddr, cleanup, err := startServer(t, test.header)
    87  			if err != nil {
    88  				t.Fatal(err)
    89  			}
    90  			defer cleanup()
    91  			if err := doHTTPHeaderTest(serverAddr, test.errCode); err != nil {
    92  				t.Error(err)
    93  			}
    94  		})
    95  	}
    96  }
    97  
    98  // Testing erroneous ResponseHeader or Trailers-only (delivered in the first HEADERS frame).
    99  func (s) TestHTTPHeaderFrameErrorHandlingInitialHeader(t *testing.T) {
   100  	for _, test := range []struct {
   101  		name    string
   102  		header  []string
   103  		errCode codes.Code
   104  	}{
   105  		{
   106  			name: "missing gRPC status",
   107  			header: []string{
   108  				":status", "403",
   109  				"content-type", "application/grpc",
   110  			},
   111  			errCode: codes.PermissionDenied,
   112  		},
   113  		{
   114  			name: "malformed grpc-status",
   115  			header: []string{
   116  				":status", "502",
   117  				"content-type", "application/grpc",
   118  				"grpc-status", "abc",
   119  			},
   120  			errCode: codes.Internal,
   121  		},
   122  		{
   123  			name: "Malformed grpc-tags-bin field",
   124  			header: []string{
   125  				":status", "502",
   126  				"content-type", "application/grpc",
   127  				"grpc-status", "0",
   128  				"grpc-tags-bin", "???",
   129  			},
   130  			errCode: codes.Unavailable,
   131  		},
   132  		{
   133  			name: "gRPC status error",
   134  			header: []string{
   135  				":status", "502",
   136  				"content-type", "application/grpc",
   137  				"grpc-status", "3",
   138  			},
   139  			errCode: codes.Unavailable,
   140  		},
   141  	} {
   142  		t.Run(test.name, func(t *testing.T) {
   143  			serverAddr, cleanup, err := startServer(t, test.header)
   144  			if err != nil {
   145  				t.Fatal(err)
   146  			}
   147  			defer cleanup()
   148  			if err := doHTTPHeaderTest(serverAddr, test.errCode); err != nil {
   149  				t.Error(err)
   150  			}
   151  		})
   152  	}
   153  }
   154  
   155  // Testing non-Trailers-only Trailers (delivered in second HEADERS frame)
   156  func (s) TestHTTPHeaderFrameErrorHandlingNormalTrailer(t *testing.T) {
   157  	tests := []struct {
   158  		name           string
   159  		responseHeader []string
   160  		trailer        []string
   161  		errCode        codes.Code
   162  	}{
   163  		{
   164  			name: "trailer missing grpc-status",
   165  			responseHeader: []string{
   166  				":status", "200",
   167  				"content-type", "application/grpc",
   168  			},
   169  			trailer: []string{
   170  				// trailer missing grpc-status
   171  				":status", "502",
   172  			},
   173  			errCode: codes.Unavailable,
   174  		},
   175  		{
   176  			name: "malformed grpc-status-details-bin field with status 404",
   177  			responseHeader: []string{
   178  				":status", "404",
   179  				"content-type", "application/grpc",
   180  			},
   181  			trailer: []string{
   182  				// malformed grpc-status-details-bin field
   183  				"grpc-status", "0",
   184  				"grpc-status-details-bin", "????",
   185  			},
   186  			errCode: codes.Unimplemented,
   187  		},
   188  		{
   189  			name: "malformed grpc-status-details-bin field with status 200",
   190  			responseHeader: []string{
   191  				":status", "200",
   192  				"content-type", "application/grpc",
   193  			},
   194  			trailer: []string{
   195  				// malformed grpc-status-details-bin field
   196  				"grpc-status", "0",
   197  				"grpc-status-details-bin", "????",
   198  			},
   199  			errCode: codes.Internal,
   200  		},
   201  	}
   202  	for _, test := range tests {
   203  		t.Run(test.name, func(t *testing.T) {
   204  			serverAddr, cleanup, err := startServer(t, test.responseHeader, test.trailer)
   205  			if err != nil {
   206  				t.Fatal(err)
   207  			}
   208  			defer cleanup()
   209  			if err := doHTTPHeaderTest(serverAddr, test.errCode); err != nil {
   210  				t.Error(err)
   211  			}
   212  		})
   213  
   214  	}
   215  }
   216  
   217  func (s) TestHTTPHeaderFrameErrorHandlingMoreThanTwoHeaders(t *testing.T) {
   218  	header := []string{
   219  		":status", "200",
   220  		"content-type", "application/grpc",
   221  	}
   222  	serverAddr, cleanup, err := startServer(t, header, header, header)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	defer cleanup()
   227  	if err := doHTTPHeaderTest(serverAddr, codes.Internal); err != nil {
   228  		t.Fatal(err)
   229  	}
   230  }
   231  
   232  func startServer(t *testing.T, headerFields ...[]string) (serverAddr string, cleanup func(), err error) {
   233  	t.Helper()
   234  
   235  	lis, err := net.Listen("tcp", "localhost:0")
   236  	if err != nil {
   237  		return "", nil, fmt.Errorf("listening on %q: %v", "localhost:0", err)
   238  	}
   239  	server := &httpServer{responses: []httpServerResponse{{trailers: headerFields}}}
   240  	server.start(t, lis)
   241  	return lis.Addr().String(), func() { lis.Close() }, nil
   242  }
   243  
   244  func doHTTPHeaderTest(lisAddr string, errCode codes.Code) error {
   245  	cc, err := grpc.NewClient(lisAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
   246  	if err != nil {
   247  		return fmt.Errorf("NewClient(%q): %v", lisAddr, err)
   248  	}
   249  	defer cc.Close()
   250  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   251  	defer cancel()
   252  	client := testgrpc.NewTestServiceClient(cc)
   253  	stream, err := client.FullDuplexCall(ctx)
   254  	if err != nil {
   255  		return fmt.Errorf("creating FullDuplex stream: %v", err)
   256  	}
   257  	if _, err := stream.Recv(); err == nil || status.Code(err) != errCode {
   258  		return fmt.Errorf("stream.Recv() = %v, want error code: %v", err, errCode)
   259  	}
   260  	return nil
   261  }
   262  

View as plain text