google.golang.org/grpc@v1.74.2/internal/transport/http_util_test.go (about)

     1  /*
     2   *
     3   * Copyright 2014 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  
    19  package transport
    20  
    21  import (
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"math"
    26  	"net"
    27  	"reflect"
    28  	"testing"
    29  	"time"
    30  )
    31  
    32  func (s) TestDecodeTimeout(t *testing.T) {
    33  	for _, test := range []struct {
    34  		// input
    35  		s string
    36  		// output
    37  		d       time.Duration
    38  		wantErr bool
    39  	}{
    40  
    41  		{"00000001n", time.Nanosecond, false},
    42  		{"10u", time.Microsecond * 10, false},
    43  		{"00000010m", time.Millisecond * 10, false},
    44  		{"1234S", time.Second * 1234, false},
    45  		{"00000001M", time.Minute, false},
    46  		{"09999999S", time.Second * 9999999, false},
    47  		{"99999999S", time.Second * 99999999, false},
    48  		{"99999999M", time.Minute * 99999999, false},
    49  		{"2562047H", time.Hour * 2562047, false},
    50  		{"2562048H", time.Duration(math.MaxInt64), false},
    51  		{"99999999H", time.Duration(math.MaxInt64), false},
    52  		{"-1S", 0, true},
    53  		{"1234x", 0, true},
    54  		{"1234s", 0, true},
    55  		{"1234", 0, true},
    56  		{"1", 0, true},
    57  		{"", 0, true},
    58  		{"9a1S", 0, true},
    59  		{"0S", 0, false}, // PROTOCOL-HTTP2.md requires positive integers, but we allow it to timeout instead
    60  		{"00000000S", 0, false},
    61  		{"000000000S", 0, true}, // PROTOCOL-HTTP2.md allows at most 8 digits
    62  	} {
    63  		d, err := decodeTimeout(test.s)
    64  		gotErr := err != nil
    65  		if d != test.d || gotErr != test.wantErr {
    66  			t.Errorf("timeoutDecode(%q) = %d, %v, want %d, wantErr=%v",
    67  				test.s, int64(d), err, int64(test.d), test.wantErr)
    68  		}
    69  	}
    70  }
    71  
    72  func (s) TestEncodeGrpcMessage(t *testing.T) {
    73  	for _, tt := range []struct {
    74  		input    string
    75  		expected string
    76  	}{
    77  		{"", ""},
    78  		{"Hello", "Hello"},
    79  		{"\u0000", "%00"},
    80  		{"%", "%25"},
    81  		{"系统", "%E7%B3%BB%E7%BB%9F"},
    82  		{string([]byte{0xff, 0xfe, 0xfd}), "%EF%BF%BD%EF%BF%BD%EF%BF%BD"},
    83  	} {
    84  		actual := encodeGrpcMessage(tt.input)
    85  		if tt.expected != actual {
    86  			t.Errorf("encodeGrpcMessage(%q) = %q, want %q", tt.input, actual, tt.expected)
    87  		}
    88  	}
    89  
    90  	// make sure that all the visible ASCII chars except '%' are not percent encoded.
    91  	for i := ' '; i <= '~' && i != '%'; i++ {
    92  		output := encodeGrpcMessage(string(i))
    93  		if output != string(i) {
    94  			t.Errorf("encodeGrpcMessage(%v) = %v, want %v", string(i), output, string(i))
    95  		}
    96  	}
    97  
    98  	// make sure that all the invisible ASCII chars and '%' are percent encoded.
    99  	for i := rune(0); i == '%' || (i >= rune(0) && i < ' ') || (i > '~' && i <= rune(127)); i++ {
   100  		output := encodeGrpcMessage(string(i))
   101  		expected := fmt.Sprintf("%%%02X", i)
   102  		if output != expected {
   103  			t.Errorf("encodeGrpcMessage(%v) = %v, want %v", string(i), output, expected)
   104  		}
   105  	}
   106  }
   107  
   108  func (s) TestDecodeGrpcMessage(t *testing.T) {
   109  	for _, tt := range []struct {
   110  		input    string
   111  		expected string
   112  	}{
   113  		{"", ""},
   114  		{"Hello", "Hello"},
   115  		{"H%61o", "Hao"},
   116  		{"H%6", "H%6"},
   117  		{"%G0", "%G0"},
   118  		{"%E7%B3%BB%E7%BB%9F", "系统"},
   119  		{"%EF%BF%BD", "�"},
   120  	} {
   121  		actual := decodeGrpcMessage(tt.input)
   122  		if tt.expected != actual {
   123  			t.Errorf("decodeGrpcMessage(%q) = %q, want %q", tt.input, actual, tt.expected)
   124  		}
   125  	}
   126  
   127  	// make sure that all the visible ASCII chars except '%' are not percent decoded.
   128  	for i := ' '; i <= '~' && i != '%'; i++ {
   129  		output := decodeGrpcMessage(string(i))
   130  		if output != string(i) {
   131  			t.Errorf("decodeGrpcMessage(%v) = %v, want %v", string(i), output, string(i))
   132  		}
   133  	}
   134  
   135  	// make sure that all the invisible ASCII chars and '%' are percent decoded.
   136  	for i := rune(0); i == '%' || (i >= rune(0) && i < ' ') || (i > '~' && i <= rune(127)); i++ {
   137  		output := decodeGrpcMessage(fmt.Sprintf("%%%02X", i))
   138  		if output != string(i) {
   139  			t.Errorf("decodeGrpcMessage(%v) = %v, want %v", fmt.Sprintf("%%%02X", i), output, string(i))
   140  		}
   141  	}
   142  }
   143  
   144  // Decode an encoded string should get the same thing back, except for invalid
   145  // utf8 chars.
   146  func (s) TestDecodeEncodeGrpcMessage(t *testing.T) {
   147  	testCases := []struct {
   148  		orig string
   149  		want string
   150  	}{
   151  		{"", ""},
   152  		{"hello", "hello"},
   153  		{"h%6", "h%6"},
   154  		{"%G0", "%G0"},
   155  		{"系统", "系统"},
   156  		{"Hello, 世界", "Hello, 世界"},
   157  
   158  		{string([]byte{0xff, 0xfe, 0xfd}), "���"},
   159  		{string([]byte{0xff}) + "Hello" + string([]byte{0xfe}) + "世界" + string([]byte{0xfd}), "�Hello�世界�"},
   160  	}
   161  	for _, tC := range testCases {
   162  		got := decodeGrpcMessage(encodeGrpcMessage(tC.orig))
   163  		if got != tC.want {
   164  			t.Errorf("decodeGrpcMessage(encodeGrpcMessage(%q)) = %q, want %q", tC.orig, got, tC.want)
   165  		}
   166  	}
   167  }
   168  
   169  const binaryValue = "\u0080"
   170  
   171  func (s) TestEncodeMetadataHeader(t *testing.T) {
   172  	for _, test := range []struct {
   173  		// input
   174  		kin string
   175  		vin string
   176  		// output
   177  		vout string
   178  	}{
   179  		{"key", "abc", "abc"},
   180  		{"KEY", "abc", "abc"},
   181  		{"key-bin", "abc", "YWJj"},
   182  		{"key-bin", binaryValue, "woA"},
   183  	} {
   184  		v := encodeMetadataHeader(test.kin, test.vin)
   185  		if !reflect.DeepEqual(v, test.vout) {
   186  			t.Fatalf("encodeMetadataHeader(%q, %q) = %q, want %q", test.kin, test.vin, v, test.vout)
   187  		}
   188  	}
   189  }
   190  
   191  func (s) TestDecodeMetadataHeader(t *testing.T) {
   192  	for _, test := range []struct {
   193  		// input
   194  		kin string
   195  		vin string
   196  		// output
   197  		vout string
   198  		err  error
   199  	}{
   200  		{"a", "abc", "abc", nil},
   201  		{"key-bin", "Zm9vAGJhcg==", "foo\x00bar", nil},
   202  		{"key-bin", "Zm9vAGJhcg", "foo\x00bar", nil},
   203  		{"key-bin", "woA=", binaryValue, nil},
   204  		{"a", "abc,efg", "abc,efg", nil},
   205  	} {
   206  		v, err := decodeMetadataHeader(test.kin, test.vin)
   207  		if !reflect.DeepEqual(v, test.vout) || !reflect.DeepEqual(err, test.err) {
   208  			t.Fatalf("decodeMetadataHeader(%q, %q) = %q, %v, want %q, %v", test.kin, test.vin, v, err, test.vout, test.err)
   209  		}
   210  	}
   211  }
   212  
   213  func (s) TestParseDialTarget(t *testing.T) {
   214  	for _, test := range []struct {
   215  		target, wantNet, wantAddr string
   216  	}{
   217  		{"unix:a", "unix", "a"},
   218  		{"unix:a/b/c", "unix", "a/b/c"},
   219  		{"unix:/a", "unix", "/a"},
   220  		{"unix:/a/b/c", "unix", "/a/b/c"},
   221  		{"unix://a", "unix", "a"},
   222  		{"unix://a/b/c", "unix", "/b/c"},
   223  		{"unix:///a", "unix", "/a"},
   224  		{"unix:///a/b/c", "unix", "/a/b/c"},
   225  		{"unix:etcd:0", "unix", "etcd:0"},
   226  		{"unix:///tmp/unix-3", "unix", "/tmp/unix-3"},
   227  		{"unix://domain", "unix", "domain"},
   228  		{"unix://etcd:0", "unix", "etcd:0"},
   229  		{"unix:///etcd:0", "unix", "/etcd:0"},
   230  		{"passthrough://unix://domain", "tcp", "passthrough://unix://domain"},
   231  		{"https://google.com:443", "tcp", "https://google.com:443"},
   232  		{"dns:///google.com", "tcp", "dns:///google.com"},
   233  		{"/unix/socket/address", "tcp", "/unix/socket/address"},
   234  	} {
   235  		gotNet, gotAddr := ParseDialTarget(test.target)
   236  		if gotNet != test.wantNet || gotAddr != test.wantAddr {
   237  			t.Errorf("ParseDialTarget(%q) = %s, %s want %s, %s", test.target, gotNet, gotAddr, test.wantNet, test.wantAddr)
   238  		}
   239  	}
   240  }
   241  
   242  type badNetworkConn struct {
   243  	net.Conn
   244  }
   245  
   246  func (c *badNetworkConn) Write([]byte) (int, error) {
   247  	return 0, io.EOF
   248  }
   249  
   250  // This test ensures Write() on a broken network connection does not lead to
   251  // an infinite loop. See https://github.com/grpc/grpc-go/issues/7389 for more details.
   252  func (s) TestWriteBadConnection(t *testing.T) {
   253  	data := []byte("test_data")
   254  	// Configure the bufWriter with a batchsize that results in data being flushed
   255  	// to the underlying conn, midway through Write().
   256  	writeBufferSize := (len(data) - 1) / 2
   257  	writer := newBufWriter(&badNetworkConn{}, writeBufferSize, getWriteBufferPool(writeBufferSize))
   258  
   259  	errCh := make(chan error, 1)
   260  	go func() {
   261  		_, err := writer.Write(data)
   262  		errCh <- err
   263  	}()
   264  
   265  	select {
   266  	case <-time.After(time.Second):
   267  		t.Fatalf("Write() did not return in time")
   268  	case err := <-errCh:
   269  		if !errors.Is(err, io.EOF) {
   270  			t.Fatalf("Write() = %v, want error presence = %v", err, io.EOF)
   271  		}
   272  	}
   273  }
   274  
   275  func BenchmarkDecodeGrpcMessage(b *testing.B) {
   276  	input := "Hello, %E4%B8%96%E7%95%8C"
   277  	want := "Hello, 世界"
   278  	b.ReportAllocs()
   279  	for i := 0; i < b.N; i++ {
   280  		got := decodeGrpcMessage(input)
   281  		if got != want {
   282  			b.Fatalf("decodeGrpcMessage(%q) = %s, want %s", input, got, want)
   283  		}
   284  	}
   285  }
   286  
   287  func BenchmarkEncodeGrpcMessage(b *testing.B) {
   288  	input := "Hello, 世界"
   289  	want := "Hello, %E4%B8%96%E7%95%8C"
   290  	b.ReportAllocs()
   291  	for i := 0; i < b.N; i++ {
   292  		got := encodeGrpcMessage(input)
   293  		if got != want {
   294  			b.Fatalf("encodeGrpcMessage(%q) = %s, want %s", input, got, want)
   295  		}
   296  	}
   297  }