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

Support grpc-web by adding Cross-Origin Resource Sharing #270

Closed
shadowmint opened this issue Feb 17, 2020 · 9 comments · Fixed by #1326
Closed

Support grpc-web by adding Cross-Origin Resource Sharing #270

shadowmint opened this issue Feb 17, 2020 · 9 comments · Fixed by #1326

Comments

@shadowmint
Copy link

shadowmint commented Feb 17, 2020

Feature Request

Crates

tonic = "0.1.1"

Motivation

When calling tonic service from grpc-web clients, the result is invariably:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://127.0.0.1:10001/routeguide.RouteGuide/ListFeatures. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

This is because the HTTP/2 request being made is preceeded by a pre-flight CORS request to determine if the request should be allowed, for more detail, background, etc. see: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

By adding this, tonic based grpc services could be called directly from grpc-web clients, without the need for a proxy such as envoy.

Proposal

When creating the server, add a section to allow CORS options to be specified, eg.

    let svc = RouteGuideServer::new(route_guide);
    Server::builder()
        .max_concurrent_streams(256)
        .tls_config(..)
        .add_service(svc)
        .serve(addr)
        .await?;

Add a new cors_config() method that allows the CORS headers to be specified, ie:

    let remote_client = "127.0.0.1:3000".parse().unwrap();
    let svc = RouteGuideServer::new(route_guide);
    Server::builder()
        .cors_config(CorsConfig { remotes: vec!(remote_client)})
        .add_service(svc)
        .serve(addr)
        .await?;

Alternatives

https://github.com/hyperium/tonic/tree/master/examples/src/interceptor shows how an interceptor can be used to process requests; however, because the OPTIONS request for CORS is a pre-flight check, the interceptor is never invoked for the request.

https://github.com/hyperium/tonic/tree/master/examples/src/hyper_warp shows how a warp server can be used along side the server implementation. However, it is unclear if this is suitable as it appears that the demonstrated implementation is A/B, where the B requests are handled entirely by the GRPC service and the A requests entirely by the warp service. ie. You cannot handle the OPTIONS request from B requests in A. However, perhaps I simply don't understand this example and it is suitable.

You could also simply require that a proxy is used, eg. envoy. However, the downside of this is that although it is probably the correct 'production' solution, just as we wish to be able to run the service locally without having a copy of apache/nginx/etc, the developer experience is very poor. Having a local webserver for frontend development on say, localhost:3000, and wanting to have a local dev server to directly connect to is an extremely common development workflow. The setup required for this is covered in reasonable detail here: https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880, although it omits the deeply irritating process of getting envoy to run on windows at all.

Ultimately, this is a developer papercut.

You setup a tonic grpc service, and out of the box, grpc-web doesn't work, and there's no work around for it other than running a local proxy server.

@alce
Copy link
Collaborator

alce commented Feb 17, 2020

Hi @shadowmint

Enabling CORS is not enough for Tonic's transport to handle grpc-web requests, since the protocol is different. We either need a proxy to translate requests/responses to/from grpc and grpc-web or a custom transport to handle grpc-web requests.

The latter solution was proposed by Lucio elsewhere and I think it's the best option. I did some work around this a while back but haven't been able to finish it.

@LucioFranco
Copy link
Member

Yeah, grpc-web is a different protocol than what tonic currently implements, so this behavior is expected. Right now, tonic only supports gRPC over http2 and if you want grpc-web support you will most likely need to use some sort of proxy. As @alce we have looked into support a different transport that can handle this but no one has really fully championed the work :)

@zancas
Copy link
Contributor

zancas commented May 20, 2020

Hi @shadowmint

Enabling CORS is not enough for Tonic's transport to handle grpc-web requests, since the protocol is different. We either need a proxy to translate requests/responses to/from grpc and grpc-web or a custom transport to handle grpc-web requests.

The latter solution was proposed by Lucio elsewhere and I think it's the best option. I did some work around this a while back but haven't been able to finish it.

@alce @LucioFranco this sounds like an interesting project. Is your initial work available somewhere?

@alce
Copy link
Collaborator

alce commented May 21, 2020

@zancas Not mine. The implementation I am using only handles grpc-web+proto unary calls. It works for me for now but publishing it would probably do more harm than good for anybody else. I'll clean it up soon-ish, I hope.

If this interests you, I could publish a gist somewhere although the code it's not ready for production and it's a bit of an eye sore.

There is also this PR #288 . I have not looked at it though but it's probably better than mine.

@onelson
Copy link

onelson commented May 21, 2020

Feeling like I don't have much to add to the conversation but when I did a proof of concept grpc-web setup with tonic, I used envoy to proxy the tonic service. You can configure the CORS headers in envoy. Having envoy in the middle took care of the conversion from http 1 -> 2, keeping clients and servers both happy without having to concern themselves with extra baggage.

I wrote a little about this here: https://github.com/onelson/e2e-rs/tree/grpc#envoy

@onsails
Copy link

onsails commented May 21, 2020

I am using gloo (which is using envoy under the hood) to expose tonic-based services to web-based clients (which use grpc-web protoc plugin) – it works like a charm.

@softdevca
Copy link

grpc-web should now be handled directly by tonic-web

@davidpdrsn
Copy link
Member

And cors can be done with https://docs.rs/tower-http/0.3.4/tower_http/cors/index.html

@LucioFranco
Copy link
Member

#1325 will fix enable to work correctly and make adding cors easier like it was before.

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