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

Current State of gRPC-Web and Handling CORS/Pre-flight Requests #394

Open
ksanderer opened this issue Dec 16, 2024 · 7 comments
Open

Current State of gRPC-Web and Handling CORS/Pre-flight Requests #394

ksanderer opened this issue Dec 16, 2024 · 7 comments
Labels

Comments

@ksanderer
Copy link

Hi there,

First of all, thank you for maintaining this project!

I have a question regarding the current state of gRPC-Web support:

  1. 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?
  2. 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!

@sleipnir
Copy link
Collaborator

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.

@ksanderer
Copy link
Author

ksanderer commented Dec 16, 2024

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:

  1. CORS support
  2. 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.
  3. Anything else?

@sleipnir
Copy link
Collaborator

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

@polvalente
Copy link
Contributor

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?

@sleipnir
Copy link
Collaborator

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.

@ksanderer
Copy link
Author

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 😅

  • 1. gRPC-Web works over both HTTP/1.1 and HTTP/2

  • 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
  • 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-Type is application/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.
  • 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 type 0x80).
        • The trailer frame contains serialized HTTP/2-style headers (e.g., grpc-status, grpc-message).
  • 6. CORS (Cross-Origin Resource Sharing)

    • Browsers enforce CORS policies, so the server must handle CORS preflight requests (OPTIONS requests) 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, OPTIONS
      • Access-Control-Allow-Headers: Content-Type, X-Grpc-Web, X-User-Agent
      • Access-Control-Expose-Headers: grpc-status, grpc-message
  • 7. Unary and Streaming RPCs

    • gRPC-Web supports both unary and server-streaming RPCs:
      • 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 UNIMPLEMENTED status.
  • 8. Error Handling

    • gRPC-Web uses the same status codes as gRPC (grpc-status) to indicate the outcome of an RPC:
      • 0: OK
      • 1: CANCELLED
      • 2: UNKNOWN
      • 3: INVALID_ARGUMENT
      • ...
    • Include the grpc-status and grpc-message fields 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.
  • 9. Compression

    • gRPC-Web supports optional compression for request and response payloads.
      • If compression is enabled, the grpc-encoding header specifies the compression algorithm (e.g., gzip).
      • Server must decompress incoming requests and compress outgoing responses if requested by the client.
    • The gRPC-Web specification for browsers discourages the use of message-level compression in favor of browser-level stream compression.
  • 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).

@sleipnir
Copy link
Collaborator

Yes, Framing, CORS, and Trailers should be the focus of an implementation, compression, base64 codecs, and metadata are already supported or partially supported

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

No branches or pull requests

3 participants