Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass Permanent Request Headers #213

Conversation

christianvozar
Copy link

Allows permanent request headers as-is and without Grpc-Metadata- prefix in metadata. This is particularly important if you are developing REST APIs where third-party developers will have expectations for headers such as Accept: and User-Agent:.

return true
}
switch hdr {
case
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I was concerned that this would be slow because of a bunch of calls to strcmp. Turns out that golang builds a binary tree at compile time and uses that to search. Cool!

golang/go#10000

@achew22
Copy link
Collaborator

achew22 commented Aug 13, 2016

@yugui What do you think of this?

@christianvozar
Copy link
Author

Not sure why cla/google is not passing. I signed the CLA 4 days ago and still not passing.

@yugui
Copy link
Member

yugui commented Aug 16, 2016

@achew22, @christianvozar
I've started to realize that there are use cases which require HTTP headers forwarded, unlike our past assumption.
So I like the overall direction of this PR.

But I wonder if we can have better handling of several HTTP headers in the whitelist.

  • Content-Type, User-Agent, TE: They are a part of grpc wire protocol.. So they are not allowed as custom metadata names.
  • Connection: It does not make much sense to forward this field and it is prohibited by RFC 2616 14.10.
  • Upgrade, Accept, Accept-Encoding, Content-Length: It is possible to forward them but it would cause an issue if the upstream actually changes the behavior based on these headers. So they should be useless for the upstream service implementation.
  • Range, If-Range: It is possible to forward them but it does not make sense because they are Content-Type-dependent.

How about the following approach?

  1. Limit the whitelist into

        "Accept-Charset",
        "Accept-Language",
        "Accept-Ranges",
        "Authorization",
        "Cache-Control",
        "Cookie",
        "Date",
        "Expect",
        "From",
        "If-Match",
        "If-Modified-Since",
        "If-None-Match",
        "If-Schedule-Tag-Match",
        "If-Unmodified-Since",
        "Max-Forwards",
        "Origin",
        "Pragma",
        "Referer",
        "Via",
        "Warning":
  2. Allow users to add more items into the whitelist with options

  3. Separately discuss why/how to forward User-Agent.

  4. Optionally support more permanent headers and their forwarding.

    • RFC 7239
    • Decrement the count in Max-Forwards, which is required by RFC 2616 14.31
    • ...

@achew22
Copy link
Collaborator

achew22 commented Aug 16, 2016

I think that whitelist is very reasonable. Let's merge this. That closes bullet 1 and unblocks @christianvozar.

I would like to think a little bit more about allowing users to add more to the whitelist. It feels like there is a good way to provide that flexibility and also to ensure the server remains as fast as can be.

What do you think about proxying User-Agent as Grpc-Gateway-User-Agent? I don't think we should pass it through as the real User-Agent for the reasons you stated above but I think it would be reasonable to claim that in the header namespace.

As for more permanent headers, I think we should wait for there to be a use case that necessitates the enhancement. Those are valid concerns. Maybe we could open an issue preemptively that says "please comment here if you are running into this?"

@yugui
Copy link
Member

yugui commented Aug 16, 2016

What do you think about proxying User-Agent as Grpc-Gateway-User-Agent?

Sounds good to me. What do you think, @christianvozar?

@christianvozar
Copy link
Author

The use case I am implementing is one where I am building an API using gRPC+JSON Gateway similar in design to one like Github's. One where a third-party developer utilizing the API might declare their User-Agent: as something specific utilized for debugging purposes I do not want the user to need to declare it as Grpc-Gateway-User-Agent in a request but if that is how it surfaces in the context metadata, that is fine by me. The same goes for Accept:, as this header is what I check for API version validation. (example: https://developer.github.com/v3/#current-version) If we want to pass Accept: to the context metadata as Grpc-Gateway-Accept: that is fine by me. Essentially, to the user of the REST gateway I want the appearance of a standard REST API without revealing the underlying usage of gRPC via prefixes.

So if we are all in agreement that @yugui 's whitelist is an appropriate one, that headers reserved for the gRPC wire protocol (User-Agent, et al.) should be passed into context metadata with a Grpc-Gateway- prefix, and that the headers that do not make sense as outlined by @yugui to forward such Connection or Upgrade should be dropped then I will whip together a commit and squash this all into one tidy PR.

@achew22 @yugui Gimme a +1 and I'll get it submitted asap.

@yugui
Copy link
Member

yugui commented Aug 17, 2016

@christianvozar Right. It does not mean that API users need to specify Grpc-Gateway-User-Agent. It just means that your backend gRPC service will receive Grpc-Gateway-User-Agent.

BTW, now I remember that the prefix Grpc- is reserved by grpc wire protocol. So can we use GrpcGateway- as an alternate prefix?

For versioning, the recommended way of implementation in gRPC is to define a different gRPC services or methods. And you can dispatch those versions in your custom http handler in your gateway.
In general, forwarding TE or Content-Type looks to be a layer violation. So I am against forwarding TE or Content-Type unless we have another concrete use case.

@christianvozar christianvozar force-pushed the feature/reserved-headers branch 2 times, most recently from eae2f5f to 815536c Compare August 18, 2016 20:51
@christianvozar
Copy link
Author

Sorry for the delayed response in getting you this. Beware NextProtos: []string{"h2"} in 1.7. Ha!

So for my use-case, as discussed previously, I am really only looking for most standard HTTP headers to be proxied to the gRPC server. That they have a prefix is totally fine, just so long as end-users/developers do not have to specify the prefix in their REST call. So to make life easier I prefix all standard HTTP Request Headers with GrpcGateway- to avoid gRPC reserved header

I did not want to put the ToLower in there but wanted to keep compatibility with bcaacb4.

Sample cURL: curl -X GET -k https://localhost:10000-H "Content-Type: application/json" -H "Accept: application/vnd.cvozar+json; version=1" -H "GrpcGateway-Sample: foo" -H "Authorization: token"

Sample Metadata Context: map[grpcgateway-authorization:[token] x-forwarded-for:[172.17.0.1] :authority:[localhost] grpcgateway-user-agent:[curl/7.43.0] grpcgateway-content-type:[application/json] x-forwarded-host:[localhost:10000] grpcgateway-accept:[application/vnd.cvozar+json; version=1]]

// permenant request headers maintained by IANA.
// http://www.iana.org/assignments/message-headers/message-headers.xml
func isPermanentHTTPHeader(hdr string) bool {
if hdr != "" && hdr[0] == ':' {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What permanent headers start with ":"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None, solid point. I was under the impression (could be wrong here) that gRPC reserved headers start with : and this was part of a check. Happy to remove it.

@christianvozar
Copy link
Author

@yugui || @achew22 Thoughts?

@achew22
Copy link
Collaborator

achew22 commented Sep 5, 2016

@christianvozar it appears that there are tests failing on Travis. Can you take a look at those?

@christianvozar christianvozar force-pushed the feature/reserved-headers branch 2 times, most recently from fc8c9c5 to 99d95a0 Compare September 14, 2016 20:33
@kalbasit
Copy link

kalbasit commented Oct 4, 2016

any updates on getting this merged?

@@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

md will never get printed. Nothing expects it in the string

@tmc
Copy link
Collaborator

tmc commented Nov 4, 2016

Given the canonical MIME header key encoding we would likely want to make this 'Grpcgateway' as the header prefix. I don't love this but we should probably keep out of the way of 'Grpc-*'.

@tmc
Copy link
Collaborator

tmc commented Nov 4, 2016

as this is it will be a breaking change as 'authorization' is passed as 'grpcgateway-authorization'

@tmc
Copy link
Collaborator

tmc commented Feb 17, 2017

Closing as #252 addresses this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants