Is there a way to specify the request type for the calls grpc-web does? I only see POST requests, I would like to use GET.
I want to use GET requests because it would be a lot easier to cache the results in a PWA service worker since there are solutions for that readily available.
The gRPC-Web protocol mandates POST requests for all requests. See https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md (which references the HTTP/2 spec: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests).
The spec actually does not specify behavior over HTTP 1.1 (did I miss it?). There is no specific mention of POST in the first document. The gRPC HTTP/2 spec, is IMHO completely orthogonal to grpc-web (otherwise grpc-web would not be needed in the first place!).
Personally I think GET should be supported on a per-request opt-in basis for exactly the reason that @pvo13 mentioned: service workers. In order to cache a response in a service worker for a POST, you have to intercept it and put it in something like IndexedDB (see https://w3c.github.io/ServiceWorker/#cache-put bullet point 4).
One issue with this however is how to encode the GET request payload.
The second paragraph in the spec is:
This document lists the differences between the two protocols. To help tracking future revisions, this document describes a delta with the protocol details specified in the native gRPC protocol.
Thus, because the native gRPC protocol mandates POST, so does grpc-web.
OK you are right I did not see the part about the delta.
Nevertheless, I tried it and it works fine.
On the client side, rather than putting the framed binary request content as the xhr/fetch body, I base64-encoded it and put it into a new header x-grpc-web-request and used method GET (implies an ~8KB request size limit if what I read about header size limits are true).
On the server, it's a fairly minimal change:
// IsGrpcWebRequest determines if a request is a gRPC-Web request by checking that the "content-type" is
// "application/grpc-web" and that the method is POST.
func (w *WrappedGrpcServer) IsGrpcWebRequest(req *http.Request) bool {
- return req.Method == http.MethodPost && strings.HasPrefix(req.Header.Get("content-type"), grpcWebContentType)
+ return strings.HasPrefix(req.Header.Get("content-type"), grpcWebContentType)
}
func hackIntoNormalGrpcRequest(req *http.Request) (*http.Request, bool) {
// Hack, this should be a shallow copy, but let's see if this works
req.ProtoMajor = 2
req.ProtoMinor = 0
contentType := req.Header.Get("content-type")
incomingContentType := grpcWebContentType
isTextFormat := strings.HasPrefix(contentType, grpcWebTextContentType)
if isTextFormat {
// body is base64-encoded: decode it; Wrap it in readerCloser so Body is still closed
decoder := base64.NewDecoder(base64.StdEncoding, req.Body)
req.Body = &readerCloser{reader: decoder, closer: req.Body}
incomingContentType = grpcWebTextContentType
}
+ if req.Method == http.MethodGet {
+ req.Method = http.MethodPost
+ in := strings.NewReader(req.Header.Get("x-grpc-web-request"))
+ decoder := base64.NewDecoder(base64.StdEncoding, in)
+ req.Body = &readerCloser{reader: decoder, closer: ioutil.NopCloser(in)}
+ }
req.Header.Set("content-type", strings.Replace(contentType, incomingContentType, grpcContentType, 1))
// Remove content-length header since it represents http1.1 payload size, not the sum of the h2
// DATA frame payload lengths. https://http2.github.io/http2-spec/#malformed This effectively
// switches to chunked encoding which is the default for h2
req.Header.Del("content-length")
return req, isTextFormat
}
Huzzah, progressive web apps with grpc-web 馃嵃 !
Haha that's a cool hack. We're unlikely to merge something like that though as I'm sure you understand. If it works for you then feel free to fork the proxy 馃榾.
Since gRPC web is an evolving protocol anyway, I think this feature should be added to it.
I created a feature request using this form: https://docs.google.com/forms/d/e/1FAIpQLSfpPkk3pM84-Bg_HFGQw8-YIM0sfapjcdPnjf16RgjuyejmbQ/viewform
Sounds good. I created a PR #935 in case other people want to experiment with this.