Skip to content

Commit

Permalink
Pass permanent HTTP request headers (#252)
Browse files Browse the repository at this point in the history
* Pass Permanent Request Headers

* Fix tests and update pr213
  • Loading branch information
tmc authored and yugui committed Jan 11, 2017
1 parent 2dbcd36 commit a51e1d5
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 13 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), gRP
* Method parameters in query string
* Enum fields in path parameter (including repeated enum fields).
* Mapping streaming APIs to newline-delimited 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.

Expand All @@ -229,7 +229,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-`)
* While configurable, the default {un,}marshaling uses [jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb) with `OrigName: true`.

# Contribution
Expand Down
8 changes: 4 additions & 4 deletions examples/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
50 changes: 47 additions & 3 deletions runtime/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
if key == "Authorization" {
// 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", MetadataPrefix, key)), val)
continue
}
if strings.HasPrefix(key, MetadataHeaderPrefix) {
Expand Down Expand Up @@ -141,3 +150,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
}
11 changes: 7 additions & 4 deletions runtime/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,17 @@ 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)
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["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["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["authorization"] = %q want %q`, got, want)
Expand Down

0 comments on commit a51e1d5

Please sign in to comment.