grpc
grpc copied to clipboard
Current State of gRPC-Web and Handling CORS/Pre-flight Requests
Hi there,
First of all, thank you for maintaining this project!
I have a question regarding the current state of gRPC-Web support:
- Is anyone using gRPC-Web in production? If so, are there any best practices or recommendations for configuring the library to work with browsers using the grpc-web-text format?
- What is the proper way to handle CORS and pre-flight requests when using gRPC-Web?
I’ve spent the past few days trying to come up with a robust solution for handling CORS and pre-flight requests without forking the repository and rewriting significant parts of the codebase, but I’ve struggled to find a clean approach.
Any guidance, examples, or advice on this topic would be greatly appreciated!
Thanks in advance for your help!
Hi @ksanderer I may be wrong but we do not yet provide support for grpc-web. Am I correct @polvalente? We have support for grpc http transcoding but not for grpc web.
We would have to investigate what would be required to implement such support. But you can use a envoy proxy to obtain this feature outside of this library.
Oh, I see now why I failed so badly 🤣. I know about Envoy Proxy, but I thought I can avoid adding extra technology to my stack.
Actually, it looks like we are quite close to this milestone. Do you understand what needs to be done to add support for gRPC-web? Maybe I can manage this and make a PR.
What I have already found is missing:
- CORS support
- Trailers for web (?) Not sure. When I patched the HTTP2 protocol to add CORS as in this issue, I started receiving errors that Trailers are missing, but I haven't delved deeply into this issue.
- Anything else?
If nothing has changed after Google passed the grpc baton I think we would have to be guided by this document here https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md
I think before going to Envoy, it looks like there's only a small amount of things we're missing. @ksanderer would you be up for at least enumerating what we need?
Yes of course @polvalente, my suggestion was for the scenario where he couldn't wait for the functionality. grpc-web is quite different and simpler than the standard protocol. It would basically encode messages in base64 and a few other small things.
I researched the topic a little (the list of features below might be incomplete and incorrect, as it is based on my high-level understanding of the codebase and the grpc-web protocol itself).
It seems I initially underestimated the amount of work involved. I marked with a checkbox the parts I have seen in the codebase that appear to be working, but I haven't performed conformance tests.
So, it looks like Envoy would be a right solution right now 😅
-
[x] 1. gRPC-Web works over both HTTP/1.1 and HTTP/2
-
[x] 2. Content-Type Handling
- For unary and streaming requests:
application/grpc-web - For requests with base64-encoded payloads (to handle binary data in browsers):
application/grpc-web-text
- For unary and streaming requests:
-
[x] 3. Base64 Encoding for gRPC-Web-Text
- gRPC-Web-Text uses base64 encoding for the request and response payloads to make them compatible with browser environments that cannot handle raw binary data.
- If the
Content-Typeisapplication/grpc-web-text, we have to:- Decode the base64-encoded request body before processing it.
- Encode the response body in base64 before sending it back to the client.
-
[ ] ??? 4. Framing of Messages
- gRPC-Web uses a framing format similar to native gRPC, but the frames can be sent over HTTP/1.1:
- Each message is prefixed with a 5-byte header:
- 1 byte: Flags (e.g., compression flag)
- 4 bytes: Length of the message
- server must parse incoming frames and construct outgoing frames in this format.
- Each message is prefixed with a 5-byte header:
- gRPC-Web uses a framing format similar to native gRPC, but the frames can be sent over HTTP/1.1:
-
[ ] 5. Trailers in the Response
- In gRPC-Web, trailers (e.g.,
grpc-status,grpc-message) are sent differently depending on the transport:- HTTP/2: Trailers are sent as native HTTP/2 trailers (HEADERS frame after the DATA frames).
- HTTP/1.1: Trailers are encoded as a special frame in the response body because HTTP/1.1 does not natively support trailers in a way accessible to browsers.
- The trailer frame is identified by setting the most significant bit of the first byte in the 5-byte header to
1(frame type0x80). - The trailer frame contains serialized HTTP/2-style headers (e.g.,
grpc-status,grpc-message).
- The trailer frame is identified by setting the most significant bit of the first byte in the 5-byte header to
- In gRPC-Web, trailers (e.g.,
-
[ ] 6. CORS (Cross-Origin Resource Sharing)
- Browsers enforce CORS policies, so the server must handle CORS preflight requests (
OPTIONSrequests) and include appropriate CORS headers in responses. - We have to add the following headers to the responses:
Access-Control-Allow-Origin: *(better to make this configurable)Access-Control-Allow-Methods: POST, OPTIONSAccess-Control-Allow-Headers: Content-Type, X-Grpc-Web, X-User-AgentAccess-Control-Expose-Headers: grpc-status, grpc-message
- Browsers enforce CORS policies, so the server must handle CORS preflight requests (
-
[ ] 7. Unary and Streaming RPCs
- gRPC-Web supports both unary and server-streaming RPCs:
- [x] Unary RPCs: A single request followed by a single response.
- [ ] ?? Server-streaming RPCs: A single request followed by multiple responses.
- [ ] gRPC-Web does not support client-streaming or bidirectional streaming RPCs - we have to reject this requests with
UNIMPLEMENTEDstatus.
- gRPC-Web supports both unary and server-streaming RPCs:
-
[ ] 8. Error Handling
- [x] gRPC-Web uses the same status codes as gRPC (
grpc-status) to indicate the outcome of an RPC:0: OK1: CANCELLED2: UNKNOWN3: INVALID_ARGUMENT- ...
- [x] Include the
grpc-statusandgrpc-messagefields in the trailers (or the trailer frame for HTTP/1.1). - [ ] Map HTTP status codes to gRPC status codes appropriately:
200 OK: RPC succeeded.400 Bad Request: Invalid request.500 Internal Server Error: Server-side error.
- [x] gRPC-Web uses the same status codes as gRPC (
-
[ ] 9. Compression
- gRPC-Web supports optional compression for request and response payloads.
- If compression is enabled, the
grpc-encodingheader specifies the compression algorithm (e.g.,gzip). - Server must decompress incoming requests and compress outgoing responses if requested by the client.
- If compression is enabled, the
- The gRPC-Web specification for browsers discourages the use of message-level compression in favor of browser-level stream compression.
- gRPC-Web supports optional compression for request and response payloads.
-
[ ] 10. Metadata Handling
- gRPC-Web supports sending and receiving metadata (key-value pairs) in the headers and trailers.
- Incoming metadata is sent as HTTP headers (e.g.,
x-grpc-web-<key>). - Outgoing metadata can be sent in the initial response headers or in the trailers.
-
[ ] 11. gRPC-Web-Specific Headers**
- Handle gRPC-Web-specific headers in requests and responses:
X-Grpc-Web: Indicates that the request is a gRPC-Web request.X-User-Agent: Optional header sent by the client (e.g.,grpc-web-javascript/0.1).
- Handle gRPC-Web-specific headers in requests and responses:
Yes, Framing, CORS, and Trailers should be the focus of an implementation, compression, base64 codecs, and metadata are already supported or partially supported
As far as I know this library does support grpc-web (and yep we use it), yes there is indeed a cors issue https://github.com/elixir-grpc/grpc/issues/237. from the top of my head, yep there might be som trailing issues/backpressure issues affecting download of huge files. However, i do think that the we are moving to http transcoding and hopefully there are no reasons to stick with grpc-web. My 2 cents
I like to mention @drowzy effort here https://github.com/elixir-grpc/grpc/pull/206#issuecomment-1186731225
@ksanderer maybe this https://github.com/elixir-grpc/grpc/pull/412 solves this issue?
Hi guys @ksanderer @AleksandarFilipov. Can you verify if the recent addition of CORS support has unlocked grpc-web or if something still needs to be done to support this?
FWIW, I'm using this in a (small) production environment. The CORS interceptor works, though I just filed a PR against it to improve its reliability (found in production). It's all quite reliable, otherwise -> the same protos are used to generate both the Elixir gRPC and the front-end Typescript gRPC-web code and they interoperate perfectly.
This environment currently has 3 gRPC endpoints that are used from the web UIs.
We have no proxies or other infra (other than the usual transparent ingress) between the browser and the Elixir gRPC endpoints, while the gRPC endpoints themselves are interacting with a variety of data sources on the back end. It all Just Works(tm).
Great @aseigo thank you. Then I'll be closing this issue once we have field evidence proving the feature. If necessary, we can reopen this or create a new issue in the future.