-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Support user configurable header forwarding & context metadata #336
Conversation
f41c8ca
to
15d0787
Compare
@tamalsaha thanks for your contribution. A couple notes:
|
Thanks @tmc .
Do you have any suggestion about that? The only passed thing I can see if the Mux
I have no options about this. I thought it could be handy. |
I would prefer the Register methods taking a variadic options slice or the mux expanding a bit in responsibility. |
@tmc, I have looked into the code and template. It seems to me that extending ServeMux might be easier way. I can also turn my matchers into |
@tmc, I think it is ready for review. I have extended ServeMux to add the matchers. |
runtime/matchers.go
Outdated
return func(mux *ServeMux) { | ||
mux.headerMatchers = append(mux.headerMatchers, func(key string) bool { | ||
return strings.HasPrefix(strings.ToLower(key), h) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be handled without the ToLower by doing an EqualFold on the head of the key. For example, https://play.golang.org/p/lh_JIYnZ0s
I only mention this because EqualFold is a bit more involved than comparing two lowercase strings. This may not really matter, though, since I think the character set available to HTTP headers is fairly restrictive (i.e., ASCII). So, nitpicking a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @nilium . I updated accordingly.
@tmc, I have added support for context annotators in my pr. Looks like we need this too! |
41d05d7
to
7af137c
Compare
@ilius, I have added support for adding metadata to context via Mux options. Does this solve your use-case? |
@tamalsaha Not sure. Does it support returning error (in REST response) before reaching grpc client? We need to read request cookie, set grpc context or return error, on each request |
I have few questions:
|
|
@ilius, in that case, you will be able to handle your use case using this pr, like below:
Then on the grpc side, you can check for JWT token in cookie and reject the request if it fails in a grpc interceptor. IMO, this is better than failing at the gateway layer for 2 reasons:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tamalsaha thanks for this! I have a few small comments but this is looking great.
runtime/matchers.go
Outdated
import "strings" | ||
|
||
// EqualMatcher performs a case-sensitive equality match for request metadata keys | ||
func EqualMatcher(s string) ServeMuxOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer a WithHeaderMatcher Option that accepts a func(string) bool
to both support more scenarios but also to have a smaller api surface area.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking further, perhaps the signature should be func(string) (string, bool)
to allow transformations in addition to simple predicate filtering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tmc , I am not sure what you mean by WithHeaderMatcher
option? Mind elaborating a bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean to something like:
type HeaderMatcherFunc func(string) (string, bool)
func WithHeaderMatcher(fn HeaderMatcherFunc) ServeMuxOption {
return func(s *ServeMux) {
s.headerMatcher = fn
}
}
runtime/mux.go
Outdated
@@ -36,12 +39,21 @@ func WithForwardResponseOption(forwardResponseOption func(context.Context, http. | |||
} | |||
} | |||
|
|||
// WithMetadata returns metadata to be passed with context. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment should elaborate a bit more
runtime/mux.go
Outdated
// NewServeMux returns a new ServeMux whose internal mapping is empty. | ||
func NewServeMux(opts ...ServeMuxOption) *ServeMux { | ||
serveMux := &ServeMux{ | ||
handlers: make(map[string][]handler), | ||
forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0), | ||
marshalers: makeMarshalerMIMERegistry(), | ||
headerMatchers: make([]func(string) bool, 0), | ||
metadataAdders: make([]func(context.Context, *http.Request) metadata.MD, 0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we call this metadataAnnotators?
@tmc, tests are passing. |
I wonder if we should expose the direction to the header matcher? |
Like a IncomingHeaderMatcher, OutgoingHeaderMatcher ? |
@tamalsaha I think those names are fine. |
Updated pr to add separate incoming/outgoing HeaderMatcher. IncomingHeaderMatcher is set to DefaultHeaderMatcher to maintain current behavior. |
LGTM |
@tamalsaha I don't understand. We have no access to cookie in our grpc server handlers. Only context and input(request). We must use context for storing token. Cookie is only for REST (http1). So we must copy the token from cookie to GRPC context when translating REST to GRPC. |
@ilius , if you want to forward the Cookie in http request to grpc, you can add an incomingHeaderMatcher for that.
If you want to forward a specific key from Cookie, you can also do that by returning that in the |
And where to put this header in the grpc context? |
It will be automatically added to the Metadata in grpc context. See LN # 66 & # 95 https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/context.go#L66 |
Thanks |
@tamalsaha Sorry, I think |
@ilius , I am not sure I understand you. Using |
Thanks, I get it now (the name mistakened me) |
This is awesome. Been waiting for 2+ months for this feature. Would like to see this in a new release. (It's kinda unfortunate this missed |
For anyone reading this and looking for a way to get a In a normal REST request you would access Cookies via With all the suggestions above we are only mapping the request headers to the gRPC metadata (FYI: cookies are transmitted in the request header) This means you have access to the raw cookie string in the metadata, not the There is no So as a workaround you may need to do something like this (code not tested): md, _ := metadata.FromIncomingContext(ctx)
rawCookies := md.Get("Grpc-Metadata-Cookie")
dummyReq, _ := http.NewRequest("", "", nil)
for _, rawCookie := range rawCookies {
dummyReq.Header.Add("Cookie", rawCookie)
}
myCookie, _ := dummyReq.Cookie("my-cookie") |
Fixes #311
@tmc / @yugui , if the general idea looks ok to you, I will add tests for this.
cc:@sadlil