github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/runtime/context_test.go (about)

     1  package runtime_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"net/http"
     7  	"reflect"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    12  	"google.golang.org/grpc/metadata"
    13  )
    14  
    15  const (
    16  	emptyForwardMetaCount = 1
    17  )
    18  
    19  func TestAnnotateContext_WorksWithEmpty(t *testing.T) {
    20  	ctx := context.Background()
    21  	expectedRPCName := "/example.Example/Example"
    22  	expectedHTTPPathPattern := "/v1"
    23  	request, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com/v1", nil)
    24  	if err != nil {
    25  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
    26  	}
    27  	request.Header.Add("Some-Irrelevant-Header", "some value")
    28  	annotated, err := runtime.AnnotateContext(ctx, runtime.NewServeMux(), request, expectedRPCName, runtime.WithHTTPPathPattern(expectedHTTPPathPattern))
    29  	if err != nil {
    30  		t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
    31  		return
    32  	}
    33  	md, ok := metadata.FromOutgoingContext(annotated)
    34  	if !ok || len(md) != emptyForwardMetaCount {
    35  		t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount, md)
    36  	}
    37  }
    38  
    39  func TestAnnotateContext_ForwardsGrpcMetadata(t *testing.T) {
    40  	ctx := context.Background()
    41  	expectedRPCName := "/example.Example/Example"
    42  	expectedHTTPPathPattern := "/v1"
    43  	request, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com/v1", nil)
    44  	if err != nil {
    45  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
    46  	}
    47  	request.Header.Add("Some-Irrelevant-Header", "some value")
    48  	request.Header.Add("Grpc-Metadata-FooBar", "Value1")
    49  	request.Header.Add("Grpc-Metadata-Foo-BAZ", "Value2")
    50  	request.Header.Add("Grpc-Metadata-foo-bAz", "Value3")
    51  	request.Header.Add("Authorization", "Token 1234567890")
    52  	annotated, err := runtime.AnnotateContext(ctx, runtime.NewServeMux(), request, expectedRPCName, runtime.WithHTTPPathPattern(expectedHTTPPathPattern))
    53  	if err != nil {
    54  		t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
    55  		return
    56  	}
    57  	md, ok := metadata.FromOutgoingContext(annotated)
    58  	if got, want := len(md), emptyForwardMetaCount+4; !ok || got != want {
    59  		t.Errorf("metadata items in context = %d want %d: %v", got, want, md)
    60  	}
    61  	if got, want := md["foobar"], []string{"Value1"}; !reflect.DeepEqual(got, want) {
    62  		t.Errorf(`md["grpcgateway-foobar"] = %q; want %q`, got, want)
    63  	}
    64  	if got, want := md["foo-baz"], []string{"Value2", "Value3"}; !reflect.DeepEqual(got, want) {
    65  		t.Errorf(`md["grpcgateway-foo-baz"] = %q want %q`, got, want)
    66  	}
    67  	if got, want := md["grpcgateway-authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) {
    68  		t.Errorf(`md["grpcgateway-authorization"] = %q want %q`, got, want)
    69  	}
    70  	if got, want := md["authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) {
    71  		t.Errorf(`md["authorization"] = %q want %q`, got, want)
    72  	}
    73  	if m, ok := runtime.RPCMethod(annotated); !ok {
    74  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
    75  	} else if m != expectedRPCName {
    76  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
    77  	}
    78  
    79  	if m, ok := runtime.HTTPPathPattern(annotated); !ok {
    80  		t.Errorf("runtime.HTTPPathPattern(annotated) failed with no value; want %s", expectedHTTPPathPattern)
    81  	} else if m != expectedHTTPPathPattern {
    82  		t.Errorf("runtime.HTTPPathPattern(annotated) failed with %s; want %s", m, expectedHTTPPathPattern)
    83  	}
    84  }
    85  
    86  func TestAnnotateContext_ForwardGrpcBinaryMetadata(t *testing.T) {
    87  	ctx := context.Background()
    88  	expectedRPCName := "/example.Example/Example"
    89  	request, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com", nil)
    90  	if err != nil {
    91  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
    92  	}
    93  
    94  	binData := []byte("\x00test-binary-data")
    95  	request.Header.Add("Grpc-Metadata-Test-Bin", base64.StdEncoding.EncodeToString(binData))
    96  
    97  	annotated, err := runtime.AnnotateContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
    98  	if err != nil {
    99  		t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
   100  		return
   101  	}
   102  	md, ok := metadata.FromOutgoingContext(annotated)
   103  	if !ok || len(md) != emptyForwardMetaCount+1 {
   104  		t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount+1, md)
   105  	}
   106  	if got, want := md["test-bin"], []string{string(binData)}; !reflect.DeepEqual(got, want) {
   107  		t.Errorf(`md["test-bin"] = %q want %q`, got, want)
   108  	}
   109  	if m, ok := runtime.RPCMethod(annotated); !ok {
   110  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   111  	} else if m != expectedRPCName {
   112  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   113  	}
   114  }
   115  
   116  func TestAnnotateContext_XForwardedFor(t *testing.T) {
   117  	ctx := context.Background()
   118  	expectedRPCName := "/example.Example/Example"
   119  	request, err := http.NewRequestWithContext(ctx, "GET", "http://bar.foo.example.com", nil)
   120  	if err != nil {
   121  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://bar.foo.example.com", err)
   122  	}
   123  	request.Header.Add("X-Forwarded-For", "192.0.2.100") // client
   124  	request.RemoteAddr = "192.0.2.200:12345"             // proxy
   125  
   126  	annotated, err := runtime.AnnotateContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   127  	if err != nil {
   128  		t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
   129  		return
   130  	}
   131  	md, ok := metadata.FromOutgoingContext(annotated)
   132  	if !ok || len(md) != emptyForwardMetaCount+1 {
   133  		t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount+1, md)
   134  	}
   135  	if got, want := md["x-forwarded-host"], []string{"bar.foo.example.com"}; !reflect.DeepEqual(got, want) {
   136  		t.Errorf(`md["host"] = %v; want %v`, got, want)
   137  	}
   138  	// Note: it must be in order client, proxy1, proxy2
   139  	if got, want := md["x-forwarded-for"], []string{"192.0.2.100, 192.0.2.200"}; !reflect.DeepEqual(got, want) {
   140  		t.Errorf(`md["x-forwarded-for"] = %v want %v`, got, want)
   141  	}
   142  	if m, ok := runtime.RPCMethod(annotated); !ok {
   143  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   144  	} else if m != expectedRPCName {
   145  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   146  	}
   147  }
   148  
   149  func TestAnnotateContext_SupportsTimeouts(t *testing.T) {
   150  	ctx := context.Background()
   151  	expectedRPCName := "/example.Example/Example"
   152  	request, err := http.NewRequestWithContext(ctx, "GET", "http://example.com", nil)
   153  	if err != nil {
   154  		t.Fatalf(`http.NewRequestWithContext(ctx, "GET", "http://example.com", nil) failed with %v; want success`, err)
   155  	}
   156  	annotated, err := runtime.AnnotateContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   157  	if err != nil {
   158  		t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
   159  		return
   160  	}
   161  	if _, ok := annotated.Deadline(); ok {
   162  		// no deadline by default
   163  		t.Errorf("annotated.Deadline() = _, true; want _, false")
   164  	}
   165  
   166  	const acceptableError = 50 * time.Millisecond
   167  	runtime.DefaultContextTimeout = 10 * time.Second
   168  	annotated, err = runtime.AnnotateContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   169  	if err != nil {
   170  		t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
   171  		return
   172  	}
   173  	deadline, ok := annotated.Deadline()
   174  	if !ok {
   175  		t.Errorf("annotated.Deadline() = _, false; want _, true")
   176  	}
   177  	if got, want := time.Until(deadline), runtime.DefaultContextTimeout; got-want > acceptableError || got-want < -acceptableError {
   178  		t.Errorf("time.Until(deadline) = %v; want %v; with error %v", got, want, acceptableError)
   179  	}
   180  
   181  	for _, spec := range []struct {
   182  		timeout string
   183  		want    time.Duration
   184  	}{
   185  		{
   186  			timeout: "17H",
   187  			want:    17 * time.Hour,
   188  		},
   189  		{
   190  			timeout: "19M",
   191  			want:    19 * time.Minute,
   192  		},
   193  		{
   194  			timeout: "23S",
   195  			want:    23 * time.Second,
   196  		},
   197  		{
   198  			timeout: "1009m",
   199  			want:    1009 * time.Millisecond,
   200  		},
   201  		{
   202  			timeout: "1000003u",
   203  			want:    1000003 * time.Microsecond,
   204  		},
   205  		{
   206  			timeout: "100000007n",
   207  			want:    100000007 * time.Nanosecond,
   208  		},
   209  	} {
   210  		request.Header.Set("Grpc-Timeout", spec.timeout)
   211  		annotated, err = runtime.AnnotateContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   212  		if err != nil {
   213  			t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
   214  			return
   215  		}
   216  		deadline, ok := annotated.Deadline()
   217  		if !ok {
   218  			t.Errorf("annotated.Deadline() = _, false; want _, true; timeout = %q", spec.timeout)
   219  		}
   220  		if got, want := time.Until(deadline), spec.want; got-want > acceptableError || got-want < -acceptableError {
   221  			t.Errorf("time.Until(deadline) = %v; want %v; with error %v; timeout= %q", got, want, acceptableError, spec.timeout)
   222  		}
   223  		if m, ok := runtime.RPCMethod(annotated); !ok {
   224  			t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   225  		} else if m != expectedRPCName {
   226  			t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   227  		}
   228  	}
   229  }
   230  func TestAnnotateContext_SupportsCustomAnnotators(t *testing.T) {
   231  	ctx := context.Background()
   232  	md1 := func(context.Context, *http.Request) metadata.MD { return metadata.New(map[string]string{"foo": "bar"}) }
   233  	md2 := func(context.Context, *http.Request) metadata.MD { return metadata.New(map[string]string{"baz": "qux"}) }
   234  	expected := metadata.New(map[string]string{"foo": "bar", "baz": "qux"})
   235  	expectedRPCName := "/example.Example/Example"
   236  	request, err := http.NewRequestWithContext(ctx, "GET", "http://example.com", nil)
   237  	if err != nil {
   238  		t.Fatalf(`http.NewRequestWithContext(ctx, "GET", "http://example.com", nil) failed with %v; want success`, err)
   239  	}
   240  	annotated, err := runtime.AnnotateContext(ctx, runtime.NewServeMux(runtime.WithMetadata(md1), runtime.WithMetadata(md2)), request, expectedRPCName)
   241  	if err != nil {
   242  		t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
   243  		return
   244  	}
   245  	actual, _ := metadata.FromOutgoingContext(annotated)
   246  	for key, e := range expected {
   247  		if a, ok := actual[key]; !ok || !reflect.DeepEqual(e, a) {
   248  			t.Errorf("metadata.MD[%s] = %v; want %v", key, a, e)
   249  		}
   250  	}
   251  	if m, ok := runtime.RPCMethod(annotated); !ok {
   252  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   253  	} else if m != expectedRPCName {
   254  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   255  	}
   256  }
   257  
   258  func TestAnnotateIncomingContext_WorksWithEmpty(t *testing.T) {
   259  	ctx := context.Background()
   260  	expectedRPCName := "/example.Example/Example"
   261  	expectedHTTPPathPattern := "/v1"
   262  	request, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com/v1", nil)
   263  	if err != nil {
   264  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
   265  	}
   266  	request.Header.Add("Some-Irrelevant-Header", "some value")
   267  	annotated, err := runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(), request, expectedRPCName, runtime.WithHTTPPathPattern(expectedHTTPPathPattern))
   268  	if err != nil {
   269  		t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   270  		return
   271  	}
   272  	md, ok := metadata.FromIncomingContext(annotated)
   273  	if !ok || len(md) != emptyForwardMetaCount {
   274  		t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount, md)
   275  	}
   276  	if m, ok := runtime.RPCMethod(annotated); !ok {
   277  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   278  	} else if m != expectedRPCName {
   279  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   280  	}
   281  }
   282  
   283  func TestAnnotateIncomingContext_ForwardsGrpcMetadata(t *testing.T) {
   284  	ctx := context.Background()
   285  	expectedRPCName := "/example.Example/Example"
   286  	expectedHTTPPathPattern := "/v1"
   287  	request, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com/v1", nil)
   288  	if err != nil {
   289  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
   290  	}
   291  	request.Header.Add("Some-Irrelevant-Header", "some value")
   292  	request.Header.Add("Grpc-Metadata-FooBar", "Value1")
   293  	request.Header.Add("Grpc-Metadata-Foo-BAZ", "Value2")
   294  	request.Header.Add("Grpc-Metadata-foo-bAz", "Value3")
   295  	request.Header.Add("Authorization", "Token 1234567890")
   296  	annotated, err := runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(), request, expectedRPCName, runtime.WithHTTPPathPattern(expectedHTTPPathPattern))
   297  	if err != nil {
   298  		t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   299  		return
   300  	}
   301  	md, ok := metadata.FromIncomingContext(annotated)
   302  	if got, want := len(md), emptyForwardMetaCount+4; !ok || got != want {
   303  		t.Errorf("metadata items in context = %d want %d: %v", got, want, md)
   304  	}
   305  	if got, want := md["foobar"], []string{"Value1"}; !reflect.DeepEqual(got, want) {
   306  		t.Errorf(`md["grpcgateway-foobar"] = %q; want %q`, got, want)
   307  	}
   308  	if got, want := md["foo-baz"], []string{"Value2", "Value3"}; !reflect.DeepEqual(got, want) {
   309  		t.Errorf(`md["grpcgateway-foo-baz"] = %q want %q`, got, want)
   310  	}
   311  	if got, want := md["grpcgateway-authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) {
   312  		t.Errorf(`md["grpcgateway-authorization"] = %q want %q`, got, want)
   313  	}
   314  	if got, want := md["authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) {
   315  		t.Errorf(`md["authorization"] = %q want %q`, got, want)
   316  	}
   317  	if m, ok := runtime.RPCMethod(annotated); !ok {
   318  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   319  	} else if m != expectedRPCName {
   320  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   321  	}
   322  	if m, ok := runtime.HTTPPathPattern(annotated); !ok {
   323  		t.Errorf("runtime.HTTPPathPattern(annotated) failed with no value; want %s", expectedHTTPPathPattern)
   324  	} else if m != expectedHTTPPathPattern {
   325  		t.Errorf("runtime.HTTPPathPattern(annotated) failed with %s; want %s", m, expectedHTTPPathPattern)
   326  	}
   327  }
   328  
   329  func TestAnnotateIncomingContext_ForwardGrpcBinaryMetadata(t *testing.T) {
   330  	ctx := context.Background()
   331  	expectedRPCName := "/example.Example/Example"
   332  	request, err := http.NewRequestWithContext(ctx, "GET", "http://www.example.com", nil)
   333  	if err != nil {
   334  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
   335  	}
   336  
   337  	binData := []byte("\x00test-binary-data")
   338  	request.Header.Add("Grpc-Metadata-Test-Bin", base64.StdEncoding.EncodeToString(binData))
   339  
   340  	annotated, err := runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   341  	if err != nil {
   342  		t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   343  		return
   344  	}
   345  	md, ok := metadata.FromIncomingContext(annotated)
   346  	if !ok || len(md) != emptyForwardMetaCount+1 {
   347  		t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount+1, md)
   348  	}
   349  	if got, want := md["test-bin"], []string{string(binData)}; !reflect.DeepEqual(got, want) {
   350  		t.Errorf(`md["test-bin"] = %q want %q`, got, want)
   351  	}
   352  	if m, ok := runtime.RPCMethod(annotated); !ok {
   353  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   354  	} else if m != expectedRPCName {
   355  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   356  	}
   357  }
   358  
   359  func TestAnnotateIncomingContext_XForwardedFor(t *testing.T) {
   360  	ctx := context.Background()
   361  	expectedRPCName := "/example.Example/Example"
   362  	request, err := http.NewRequestWithContext(ctx, "GET", "http://bar.foo.example.com", nil)
   363  	if err != nil {
   364  		t.Fatalf("http.NewRequestWithContext(ctx, %q, %q, nil) failed with %v; want success", "GET", "http://bar.foo.example.com", err)
   365  	}
   366  	request.Header.Add("X-Forwarded-For", "192.0.2.100") // client
   367  	request.RemoteAddr = "192.0.2.200:12345"             // proxy
   368  
   369  	annotated, err := runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   370  	if err != nil {
   371  		t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   372  		return
   373  	}
   374  	md, ok := metadata.FromIncomingContext(annotated)
   375  	if !ok || len(md) != emptyForwardMetaCount+1 {
   376  		t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount+1, md)
   377  	}
   378  	if got, want := md["x-forwarded-host"], []string{"bar.foo.example.com"}; !reflect.DeepEqual(got, want) {
   379  		t.Errorf(`md["host"] = %v; want %v`, got, want)
   380  	}
   381  	// Note: it must be in order client, proxy1, proxy2
   382  	if got, want := md["x-forwarded-for"], []string{"192.0.2.100, 192.0.2.200"}; !reflect.DeepEqual(got, want) {
   383  		t.Errorf(`md["x-forwarded-for"] = %v want %v`, got, want)
   384  	}
   385  	if m, ok := runtime.RPCMethod(annotated); !ok {
   386  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   387  	} else if m != expectedRPCName {
   388  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   389  	}
   390  }
   391  
   392  func TestAnnotateIncomingContext_SupportsTimeouts(t *testing.T) {
   393  	// While run all test, TestAnnotateContext_SupportsTimeouts() will change the DefaultContextTimeout, so reset it to zero.
   394  	runtime.DefaultContextTimeout = 0 * time.Second
   395  	expectedRPCName := "/example.Example/Example"
   396  	ctx := context.Background()
   397  	request, err := http.NewRequestWithContext(ctx, "GET", "http://example.com", nil)
   398  	if err != nil {
   399  		t.Fatalf(`http.NewRequestWithContext(ctx, "GET", "http://example.com", nil) failed with %v; want success`, err)
   400  	}
   401  	annotated, err := runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   402  	if err != nil {
   403  		t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   404  		return
   405  	}
   406  	if _, ok := annotated.Deadline(); ok {
   407  		// no deadline by default
   408  		t.Errorf("annotated.Deadline() = _, true; want _, false")
   409  	}
   410  
   411  	const acceptableError = 50 * time.Millisecond
   412  	runtime.DefaultContextTimeout = 10 * time.Second
   413  	annotated, err = runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   414  	if err != nil {
   415  		t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   416  		return
   417  	}
   418  	deadline, ok := annotated.Deadline()
   419  	if !ok {
   420  		t.Errorf("annotated.Deadline() = _, false; want _, true")
   421  	}
   422  	if got, want := time.Until(deadline), runtime.DefaultContextTimeout; got-want > acceptableError || got-want < -acceptableError {
   423  		t.Errorf("time.Until(deadline) = %v; want %v; with error %v", got, want, acceptableError)
   424  	}
   425  
   426  	for _, spec := range []struct {
   427  		timeout string
   428  		want    time.Duration
   429  	}{
   430  		{
   431  			timeout: "17H",
   432  			want:    17 * time.Hour,
   433  		},
   434  		{
   435  			timeout: "19M",
   436  			want:    19 * time.Minute,
   437  		},
   438  		{
   439  			timeout: "23S",
   440  			want:    23 * time.Second,
   441  		},
   442  		{
   443  			timeout: "1009m",
   444  			want:    1009 * time.Millisecond,
   445  		},
   446  		{
   447  			timeout: "1000003u",
   448  			want:    1000003 * time.Microsecond,
   449  		},
   450  		{
   451  			timeout: "100000007n",
   452  			want:    100000007 * time.Nanosecond,
   453  		},
   454  	} {
   455  		request.Header.Set("Grpc-Timeout", spec.timeout)
   456  		annotated, err = runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(), request, expectedRPCName)
   457  		if err != nil {
   458  			t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   459  			return
   460  		}
   461  		deadline, ok := annotated.Deadline()
   462  		if !ok {
   463  			t.Errorf("annotated.Deadline() = _, false; want _, true; timeout = %q", spec.timeout)
   464  		}
   465  		if got, want := time.Until(deadline), spec.want; got-want > acceptableError || got-want < -acceptableError {
   466  			t.Errorf("time.Until(deadline) = %v; want %v; with error %v; timeout= %q", got, want, acceptableError, spec.timeout)
   467  		}
   468  		if m, ok := runtime.RPCMethod(annotated); !ok {
   469  			t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   470  		} else if m != expectedRPCName {
   471  			t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   472  		}
   473  	}
   474  }
   475  func TestAnnotateIncomingContext_SupportsCustomAnnotators(t *testing.T) {
   476  	ctx := context.Background()
   477  	md1 := func(context.Context, *http.Request) metadata.MD { return metadata.New(map[string]string{"foo": "bar"}) }
   478  	md2 := func(context.Context, *http.Request) metadata.MD { return metadata.New(map[string]string{"baz": "qux"}) }
   479  	expected := metadata.New(map[string]string{"foo": "bar", "baz": "qux"})
   480  	expectedRPCName := "/example.Example/Example"
   481  	request, err := http.NewRequestWithContext(ctx, "GET", "http://example.com", nil)
   482  	if err != nil {
   483  		t.Fatalf(`http.NewRequestWithContext(ctx, "GET", "http://example.com", nil) failed with %v; want success`, err)
   484  	}
   485  	annotated, err := runtime.AnnotateIncomingContext(ctx, runtime.NewServeMux(runtime.WithMetadata(md1), runtime.WithMetadata(md2)), request, expectedRPCName)
   486  	if err != nil {
   487  		t.Errorf("runtime.AnnotateIncomingContext(ctx, %#v) failed with %v; want success", request, err)
   488  		return
   489  	}
   490  	actual, _ := metadata.FromIncomingContext(annotated)
   491  	for key, e := range expected {
   492  		if a, ok := actual[key]; !ok || !reflect.DeepEqual(e, a) {
   493  			t.Errorf("metadata.MD[%s] = %v; want %v", key, a, e)
   494  		}
   495  	}
   496  	if m, ok := runtime.RPCMethod(annotated); !ok {
   497  		t.Errorf("runtime.RPCMethod(annotated) failed with no value; want %s", expectedRPCName)
   498  	} else if m != expectedRPCName {
   499  		t.Errorf("runtime.RPCMethod(annotated) failed with %s; want %s", m, expectedRPCName)
   500  	}
   501  }