From f107349baf819fffecc84dfe03b17779a5ff3775 Mon Sep 17 00:00:00 2001 From: "Christian R. Vozar" Date: Wed, 10 Aug 2016 17:30:46 -0500 Subject: [PATCH 1/2] Pass Permanent Request Headers --- runtime/context.go | 39 +++++++++++++++++++++++++++++++++++++-- runtime/context_test.go | 15 ++++++++------- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/runtime/context.go b/runtime/context.go index f248c738b23..1f9f33505bc 100644 --- a/runtime/context.go +++ b/runtime/context.go @@ -52,8 +52,8 @@ func AnnotateContext(ctx context.Context, req *http.Request) (context.Context, e for key, vals := range req.Header { for _, val := range vals { - if key == "Authorization" { - pairs = append(pairs, "authorization", val) + if isPermanentHTTPHeader(key) { + pairs = append(pairs, strings.ToLower(fmt.Sprintf("%s%s", metadataHeaderPrefix, key)), val) continue } if strings.HasPrefix(key, MetadataHeaderPrefix) { @@ -141,3 +141,38 @@ func timeoutUnitToDuration(u uint8) (d time.Duration, ok bool) { } return } + +// isPermanentHTTPHeader checks whether hdr belongs to the list of +// permenant request headers maintained by IANA. +// http://www.iana.org/assignments/message-headers/message-headers.xml +func isPermanentHTTPHeader(hdr string) bool { + switch hdr { + case + "Accept", + "Accept-Charset", + "Accept-Language", + "Accept-Ranges", + "Authorization", + "Cache-Control", + "Content-Type", + "Cookie", + "Date", + "Expect", + "From", + "Host", + "If-Match", + "If-Modified-Since", + "If-None-Match", + "If-Schedule-Tag-Match", + "If-Unmodified-Since", + "Max-Forwards", + "Origin", + "Pragma", + "Referer", + "User-Agent", + "Via", + "Warning": + return true + } + return false +} diff --git a/runtime/context_test.go b/runtime/context_test.go index abc1873fad9..9b30438b38a 100644 --- a/runtime/context_test.go +++ b/runtime/context_test.go @@ -1,6 +1,7 @@ package runtime_test import ( + "fmt" "net/http" "reflect" "testing" @@ -41,9 +42,9 @@ func TestAnnotateContext_ForwardsGrpcMetadata(t *testing.T) { t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err) } request.Header.Add("Some-Irrelevant-Header", "some value") - request.Header.Add("Grpc-Metadata-FooBar", "Value1") - request.Header.Add("Grpc-Metadata-Foo-BAZ", "Value2") - request.Header.Add("Grpc-Metadata-foo-bAz", "Value3") + request.Header.Add("GrpcGateway-FooBar", "Value1") + request.Header.Add("GrpcGateway-Foo-BAZ", "Value2") + request.Header.Add("GrpcGateway-foo-bAz", "Value3") request.Header.Add("Authorization", "Token 1234567890") annotated, err := runtime.AnnotateContext(ctx, request) if err != nil { @@ -52,16 +53,16 @@ func TestAnnotateContext_ForwardsGrpcMetadata(t *testing.T) { } md, ok := metadata.FromContext(annotated) if got, want := len(md), emptyForwardMetaCount+3; !ok || got != want { - t.Errorf("Expected %d metadata items in context; got %d", got, want) + t.Errorf("Expected %d metadata items in context; got %d", got, want, md) } if got, want := md["foobar"], []string{"Value1"}; !reflect.DeepEqual(got, want) { - t.Errorf(`md["foobar"] = %q; want %q`, got, want) + t.Errorf(`md["GrpcGateway-foobar"] = %q; want %q`, got, want) } if got, want := md["foo-baz"], []string{"Value2", "Value3"}; !reflect.DeepEqual(got, want) { - t.Errorf(`md["foo-baz"] = %q want %q`, got, want) + t.Errorf(`md["GrpcGateway-foo-baz"] = %q want %q`, got, want) } if got, want := md["authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) { - t.Errorf(`md["authorization"] = %q want %q`, got, want) + t.Errorf(`md["GrpcGateway-authorization"] = %q want %q`, got, want) } } From 5570e2c9e644d058775cf9ce0d70399abf214f15 Mon Sep 17 00:00:00 2001 From: Travis Cline Date: Thu, 3 Nov 2016 18:07:23 -0700 Subject: [PATCH 2/2] Fix tests and update pr213 --- README.md | 5 +++-- examples/integration_test.go | 8 ++++---- runtime/context.go | 15 ++++++++++++--- runtime/context_test.go | 20 +++++++++++--------- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 718047f3234..a8bb5cc2b6c 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), gRP * Method parameters in request path * Method parameters in query string * Mapping streaming APIs to JSON streams -* Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata +* Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata (prefixed with `grpcgateway-`) * Optionally emitting API definition for [Swagger](http://swagger.io). * Setting [gRPC timeouts](http://www.grpc.io/docs/guides/wire.html) through inbound HTTP `Grpc-Timeout` header. @@ -228,7 +228,8 @@ But patch is welcome. * HTTP request source IP is added as `X-Forwarded-For` gRPC request header * HTTP request host is added as `X-Forwarded-Host` gRPC request header * HTTP `Authorization` header is added as `authorization` gRPC request header -* Remaining HTTP header keys are prefixed with `Grpc-Metadata-` and added with their values to gRPC request header +* Remaining Permanent HTTP header keys (as specified by the IANA [here](http://www.iana.org/assignments/message-headers/message-headers.xhtml) are prefixed with `grpcgateway-` and added with their values to gRPC request header +* HTTP headers that start with 'Grpc-Metadata-' are mapped to gRPC metadata (prefixed with `grpcgateway-`) # Contribution See [CONTRIBUTING.md](http://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md). diff --git a/examples/integration_test.go b/examples/integration_test.go index ba011497b6a..826fbddc119 100644 --- a/examples/integration_test.go +++ b/examples/integration_test.go @@ -128,10 +128,10 @@ func testEchoBody(t *testing.T) { } if got, want := resp.Header.Get("Grpc-Metadata-Foo"), "foo1"; got != want { - t.Errorf("Grpc-Header-Foo was %q, wanted %q", got, want) + t.Errorf("Grpc-Metadata-Foo was %q, wanted %q", got, want) } if got, want := resp.Header.Get("Grpc-Metadata-Bar"), "bar1"; got != want { - t.Errorf("Grpc-Header-Bar was %q, wanted %q", got, want) + t.Errorf("Grpc-Metadata-Bar was %q, wanted %q", got, want) } if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want { @@ -369,7 +369,7 @@ func testABEBulkCreate(t *testing.T) { } if got, want := resp.Header.Get("Grpc-Metadata-Count"), fmt.Sprintf("%d", count); got != want { - t.Errorf("Grpc-Header-Count was %q, wanted %q", got, want) + t.Errorf("Grpc-Metadata-Count was %q, wanted %q", got, want) } if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want { @@ -518,7 +518,7 @@ func testABEList(t *testing.T) { value := resp.Header.Get("Grpc-Metadata-Count") if value == "" { - t.Errorf("Grpc-Header-Count should not be empty") + t.Errorf("Grpc-Metadata-Count should not be empty") } count, err := strconv.Atoi(value) diff --git a/runtime/context.go b/runtime/context.go index 1f9f33505bc..98eeb44c9a1 100644 --- a/runtime/context.go +++ b/runtime/context.go @@ -15,12 +15,17 @@ import ( "google.golang.org/grpc/metadata" ) -// MetadataHeaderPrefix is prepended to HTTP headers in order to convert them to -// gRPC metadata for incoming requests processed by grpc-gateway +// MetadataHeaderPrefix is the http prefix that represents custom metadata +// parameters to or from a gRPC call. const MetadataHeaderPrefix = "Grpc-Metadata-" + +// MetadataPrefix is the prefix for grpc-gateway supplied custom metadata fields. +const MetadataPrefix = "grpcgateway-" + // MetadataTrailerPrefix is prepended to gRPC metadata as it is converted to // HTTP headers in a response handled by grpc-gateway const MetadataTrailerPrefix = "Grpc-Trailer-" + const metadataGrpcTimeout = "Grpc-Timeout" const xForwardedFor = "X-Forwarded-For" @@ -52,8 +57,12 @@ func AnnotateContext(ctx context.Context, req *http.Request) (context.Context, e for key, vals := range req.Header { for _, val := range vals { + // For backwards-compatibility, pass through 'authorization' header with no prefix. + if strings.ToLower(key) == "authorization" { + pairs = append(pairs, "authorization", val) + } if isPermanentHTTPHeader(key) { - pairs = append(pairs, strings.ToLower(fmt.Sprintf("%s%s", metadataHeaderPrefix, key)), val) + pairs = append(pairs, strings.ToLower(fmt.Sprintf("%s%s", MetadataPrefix, key)), val) continue } if strings.HasPrefix(key, MetadataHeaderPrefix) { diff --git a/runtime/context_test.go b/runtime/context_test.go index 9b30438b38a..2f3b02393db 100644 --- a/runtime/context_test.go +++ b/runtime/context_test.go @@ -1,7 +1,6 @@ package runtime_test import ( - "fmt" "net/http" "reflect" "testing" @@ -42,9 +41,9 @@ func TestAnnotateContext_ForwardsGrpcMetadata(t *testing.T) { t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err) } request.Header.Add("Some-Irrelevant-Header", "some value") - request.Header.Add("GrpcGateway-FooBar", "Value1") - request.Header.Add("GrpcGateway-Foo-BAZ", "Value2") - request.Header.Add("GrpcGateway-foo-bAz", "Value3") + request.Header.Add("Grpc-Metadata-FooBar", "Value1") + request.Header.Add("Grpc-Metadata-Foo-BAZ", "Value2") + request.Header.Add("Grpc-Metadata-foo-bAz", "Value3") request.Header.Add("Authorization", "Token 1234567890") annotated, err := runtime.AnnotateContext(ctx, request) if err != nil { @@ -52,17 +51,20 @@ func TestAnnotateContext_ForwardsGrpcMetadata(t *testing.T) { return } md, ok := metadata.FromContext(annotated) - if got, want := len(md), emptyForwardMetaCount+3; !ok || got != want { - t.Errorf("Expected %d metadata items in context; got %d", got, want, md) + if got, want := len(md), emptyForwardMetaCount+4; !ok || got != want { + t.Errorf("metadata items in context = %d want %d: %v", got, want, md) } if got, want := md["foobar"], []string{"Value1"}; !reflect.DeepEqual(got, want) { - t.Errorf(`md["GrpcGateway-foobar"] = %q; want %q`, got, want) + t.Errorf(`md["grpcgateway-foobar"] = %q; want %q`, got, want) } if got, want := md["foo-baz"], []string{"Value2", "Value3"}; !reflect.DeepEqual(got, want) { - t.Errorf(`md["GrpcGateway-foo-baz"] = %q want %q`, got, want) + t.Errorf(`md["grpcgateway-foo-baz"] = %q want %q`, got, want) + } + if got, want := md["grpcgateway-authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) { + t.Errorf(`md["grpcgateway-authorization"] = %q want %q`, got, want) } if got, want := md["authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) { - t.Errorf(`md["GrpcGateway-authorization"] = %q want %q`, got, want) + t.Errorf(`md["authorization"] = %q want %q`, got, want) } }