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

Interceptor #221

Closed
ernestoalejo opened this issue Sep 5, 2016 · 14 comments
Closed

Interceptor #221

ernestoalejo opened this issue Sep 5, 2016 · 14 comments

Comments

@ernestoalejo
Copy link

Intercept calls the same way you can adding an UnaryInterceptor to a GRPC server. This will serve two purposes in my use case:

  • Add some custom monitoring capabilities.
  • Authenticate the request using custom logic.

I know I can add a plain HTTP middleware, but it won't contain the name of the method or the proto request that will be sent. I would like to access both the HTTP request headers and the RPC info.

@yugui
Copy link
Member

yugui commented Sep 10, 2016

That's an interesting use case. Probably we can add it as a custom option if it is really necessary.

Could you explain more about the reason why / the context in which you need to access to both of the HTTP headers and RPC info at the same time?

@ernestoalejo
Copy link
Author

Different methods have different permissions/modules/checks. Access to the HTTP header is needed to get the authorization (it may be a bearer token, or a custom header X-MyAuth) and perhaps the URL to extract useful info (like the module begin called extracted from the prefix, which is not part of the proto message). Access to the writer is needed to reject the call with custom JSON responses.

This helps building an API gateway that checks everything before letting the request in so you can simplify the microservices code behind it.

I have only found two alternatives to this use case right now:

  • Use a standard HTTP middleware that parses the URL, finds the the associated proto file using protobuf reflection techniques, parses the gzipped representation and applies any custom options of the method. If everything is correct URL will be parsed again by grpc-gateway to make the call.
  • Generate another GRPC-in GRPC-out proxy with a custom compiler plugin that checks the authorization, redirect the gateway to that instead of the real service. This will probably be easier with better support of a tool like grpc-proxy

@ericlagergren
Copy link

ericlagergren commented Oct 6, 2016

We had a very similar issue so I hacked up this in 20 minutes... which allows us to do this:

var mapping = tables.MakeMapping( ... )

func authHandler(h http.Handler) http.Handler {
...
    ep, ok := mapping.Get(r.URL.Path)
    if !ok {
        http.NotFound(w, r)
        return
    }
    act, ok := ep.Find(r.Method)
...
// Auth cookies, ACLs, etc. 
}

Which is, I think, what you want. Not perfect by any means and I'd love baked-in support.

@philipithomas
Copy link
Contributor

This would also be helpful so that swagger json can be served in the API!

@mattolson
Copy link

Has anyone started work on this? I don't see a PR for it. I also have a need for this and will submit a PR if people think it's a useful addition.

cc @yugui @tmc

@tamalsaha
Copy link
Collaborator

@mattolson, can you tell us about your use-case?

@mattolson
Copy link

I have some protected authorization fields in the request message that need to be set by the gateway.

Initially, I thought I could simply overwrite the http query params and let grpc-gateway set them for me in the grpc request message. However, the logic in the gateway code template is such that it looks at the http annotation to determine whether to pull params from body, path params, or query string, or some combination of the three, and there is no reliable way to know in advance where I should insert this data in order for it to be marshalled correctly to the request message. For example, if the http annotation says body: "*", the generated code does not include the call to PopulateQueryParameters.

What I'm proposing is a way to register a method that can be called after grpc-gateway constructs the request message but before it is sent. It could be inserted here.

@tamalsaha
Copy link
Collaborator

Have you considered adding that in the context as Metadata? Example:

gwMux := gwrt.NewServeMux(
	gwrt.WithMetadata(func(c context.Context, req *http.Request) metadata.MD {
		return metadata.Pairs("x-forwarded-method", req.Method)
	}),
)

@mattolson
Copy link

Our protos are already defined and require this data to be set in the request message, not the gRPC metadata. I'm looking for a way to reliably set this data on the request message, and also overwrite anything that might come in on the http request. Providing a hook to mutate the request message just before it is sent would work for us, or possibly to change the logic around pulling data from http params, but that would violate the spec for the http annotation.

@tamalsaha
Copy link
Collaborator

tamalsaha commented Jun 21, 2017

IMHO, grpc-gateway should be application logic agnostic and should not allow users to introduce custom logic at the gateway layer. In a future world, where everything can be done in http/2, you will not need grpc gateway. So, any custom logic you need should be at the grpc side.

I am still bit confused about your use-case. A concrete example might help. You said Our protos are already defined and require this data to be set in the request message, not the gRPC metadata. I'm looking for a way to reliably set this data on the request message, and also overwrite anything that might come in on the http request. - One way, I can think of this can be done is:

  • Use WithIncomingHeaderMatcher to pass headers from original request
  • Use WithMetadata to add any metadata that might be lost in the conversion process.
  • Use a unary interceptor at the grpc server side to set this data on the request message, and also overwrite anything that might come in on the http request. . As you may know, you can chain interceptors at the grpc layer. So, as long as you run this fixer interceptor before other interceptors, you should be able to keep all the logic at the grpc server side.

@mattolson
Copy link

I guess it depends on how we think of grpc-gateway. Is it a gateway, or a proxy? If a proxy, it makes sense that we'd not want to add any application logic. If a gateway, we should be flexible and allow for custom transformation.

@tamalsaha
Copy link
Collaborator

:) I don't know.

The one big reason I prefer to put all my logic behind grpc server is that if someone calls the http endpoint directly, they can expect same behavior they will get if they went through grpc-gateway. So, I guess I treat this as a proxy.

@mattolson
Copy link

I realize now that this discussion is moot. There is already support for client side interceptors if you are using recent versions of gRPC. All you need to do is register your interceptor in the DialOptions for the connection. This gives you full access to the context and the message constructed by grpc-gateway just before the request is sent.

@achew22
Copy link
Collaborator

achew22 commented Dec 14, 2017

Given @mattolson's latest comment closing. Feel free to reopen if you have more issues.

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

No branches or pull requests

7 participants