gRPC-web support for GWT/J2CL projects - starts with the basic gRPC APIs and stubs, and provides both replacements for those jars, plus gRPC/gRPC-web Channel implementations.
Generated from the Java projects for grpc-api, grpc-stub, grpc-protobuf, and grpc-protobuf-lite. Makes use of standard OpenRewrite rules and some rules specifically written to simplify projects for GWT.
A future version might split these dependencies into separate jars, but for now a subset of the original classes from all four are included in a single jar.
To simplify the effort of transforming the implementation, two features have been removed. Both technically could be restored, but at a glance there doesn't seem to be an obvious need.
At present, there is no ASCII charset in GWT, so it is assumed that the server is correctly sending ascii status and metadata values. All valid ascii (e.g. below 128) are equal to their corresponding utf8 value, so there should be no difference in behavior.
BinaryStreamMarshaller has also been effectively removed, along with the Metadata.Key.of() overload that supports it.
The current implementation used Class.isInstance to check which marshaller was in use, though if required this could be
reimplemented without any reflection at all.
gRPC is marketed as a "universal" RPC framework that runs in "any environment", but in practice it is entirely unusable in browsers. Instead, gRPC-web offers a few small changes over gRPC to make it mostly compatible with browsers:
- http/1.1 is supported instead of just h2. In most browsers this limits the number of concurrent streams (usually
6) to a single server, so h2 is still desired for most use cases.
- Additionally, browsers do not support h2c (h2 over cleartext), so gRPC-web over h2 is limited to TLS connections. This can make localhost or dev/staging deployments difficult to use.
- Rather than HTTP trailers, a final body payload can be read as if it were an http/1 headers block. This doesn't apply to trailers-only responses
- The
user-agentheader cannot be controlled by clients in a browser runtime, sox-user-agentshould be used instead. - Since all of this adds up to "not actually gRPC", the
content-typeheader should be prefixed withapplication/grpc-weborapplication/grpc-web-text.
These limitations are intended to "make it easy for a proxy to translate between the protocols as this is the most
likely deployment model." Unfortunately, they only cover a subset of the limitations that browsers impose, so it isn't
actually possible to run a proxy which can translate arbitrary gRPC services. The browser's fetch() implementation
does not support any client-streaming use cases, which limits clients from participating in either bidirectional
streaming or client streaming RPCs. At the time the spec was written it was believed that by 2019 this would be resolved,
and was later revised (still with a
two-year estimate) to instead be part of the streams spec. While Chrome does now technically support streaming uploads,
it is required that this be done in "half duplex" mode, which still prevents bidirectional streams from being useful.
- https://fetch.spec.whatwg.org/#ref-for-dom-requestinit-duplex
- whatwg/fetch#1254
- https://bugzilla.mozilla.org/show_bug.cgi?id=1387483
For these reasons, other gRPC clients such as (improbable-eng/grpc-web)[https://github.com/improbable-eng/grpc-web/] have included a websocket transport for gRPC messages. Unfortunately, that repository is no longer maintained, and some features we have found to be necessary were never shipped or never implemented.
A simple fetch() implementation is provided which supports unary and server-streaming calls, but by itself cannot
support other streaming calls.
This library will include two websocket transports - the first is compatible with improbable-eng/grpc-web using one websocket per stream, and the second supports using a single websocket for multiple streams to the same server (in roughly the same way that http2 would do with streams on a single socket). The first is included for compatibility with any existing proxies that support this feature - we mostly encourage the use of the second where required.
Another implementation will be provided that uses server-streaming fetch() requests to receive messages for a given
stream, and a unary fetch() to send messages. These can be paired with a server-side tools to modify a Java
BindableService to include pairs of gRPC methods which will be treated by the real server method as a single streaming
method. Some implementation details are likely to be left out, specifically around handling the state of pairing up
messages etc, and naturally any reverse proxy would be required to forward all calls to the same server to be handled
uniformly.
Technically, a Channel implementation can be written for non-browser clients with better h2 support, and that will be gRPC proper, rather than gRPC-web. Node.js for example has a serviceable h2 implementation that can be used in Electron apps or server side - it additionally supports bidirectional streaming, so a Channel implementation will be easier to provide without any workarounds. At this time, this project does not provide one.
Add com.vertispan.grpc:grpc-gwt to your project dependencies, replacing io.grpc:grpc-* dependencies. The
version will be based on the grpc-java build being used, with an integer suffix to allow for packaging changes. For
example, current released versions:
| grpc-java | grpc-gwt | Description |
|---|---|---|
| 1.63.1 | 1.63.1-1 | Initial release |
Replace/exclude any existing grpc-java dependencies with this library. Add an inherits in your project's GWT module:
<inherits name="io.grpc.Grpc" />Then, generate your own stubs as normal with protoc, taking care to only use the async stubs.
Additionally, one or more io.grpc.Channel implementation should be picked from the provided dependencies, and passed to
the newStub(..) method when creating your generated client service.
A Channel implementation that uses the browser's fetch() API to make requests. This supports unary and server-streaming
calls.
Add com.vertispan.grpc:grpc-web-gwt-fetch to your project dependencies. Then, add an inherits in your project's GWT module:
<inherits name="com.vertispan.grpc.fetch.Fetch" />This can be built simply with mvn install. The clean goal will remove the generated sources, so when changing rewrite
rules or grpc versions, a mvn clean install may be required.