I noticed that when upgrading my gRPC client from Vert.x 4.0.2 to 4.0.3, then it is not able to handle properly the HTTP responses with a status different than 200 (at least with status 401 and 403).
Indeed gRPC relies on HTTP/2 for its transport and before reaching the gRPC server, the gRPC request can go through multiple intermediaries (HTTP proxies). And such intermediary can check the incoming HTTP request and decide to forward it or to stop it by sending a plain HTTP response. The client then can map the HTTP status code to a gRPC one, as described on
https://grpc.github.io/grpc/core/md_doc_http-grpc-status-mapping.html .
The setup to reproduce is pretty simple:
- have a plain HTTP server that answers only 401
- have a gRPC client that send a gRPC request to that host.
With Vert.x 4.0.2 that works as expected: a StatusRuntimeException
is raised and the Status
attached to this Exception
is UNAUTHENTICATED
. Here is the output:
Could not reach server UNAUTHENTICATED: HTTP status code 401
invalid content-type: null
trailers: Metadata(:status=401,content-length=0)
Status: Status{code=UNAUTHENTICATED, description=HTTP status code 401
invalid content-type: null
trailers: Metadata(:status=401,content-length=0), cause=null}
With Vert.x 4.0.3 that does not work so well: StatusRuntimeException
is raised but the Status
attached to this Exception
is UNKNOWN
and we have the following traces:
Could not reach server UNKNOWN
Status: Status{code=UNKNOWN, description=null, cause=java.lang.UnsupportedOperationException
at io.grpc.netty.AbstractHttp2Headers.setLong(AbstractHttp2Headers.java:465)
at io.grpc.netty.AbstractHttp2Headers.setLong(AbstractHttp2Headers.java:26)
at io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder$FrameReadListener.onHeadersRead(DefaultHttp2ConnectionDecoder.java:403)
at io.netty.handler.codec.http2.Http2InboundFrameLogger$1.onHeadersRead(Http2InboundFrameLogger.java:65)
at io.netty.handler.codec.http2.DefaultHttp2FrameReader$1.processFragment(DefaultHttp2FrameReader.java:457)
at io.netty.handler.codec.http2.DefaultHttp2FrameReader.readHeadersFrame(DefaultHttp2FrameReader.java:464)
at io.netty.handler.codec.http2.DefaultHttp2FrameReader.processPayloadState(DefaultHttp2FrameReader.java:254)
at io.netty.handler.codec.http2.DefaultHttp2FrameReader.readFrame(DefaultHttp2FrameReader.java:160)
at io.netty.handler.codec.http2.Http2InboundFrameLogger.readFrame(Http2InboundFrameLogger.java:41)
at io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder.decodeFrame(DefaultHttp2ConnectionDecoder.java:181)
at io.netty.handler.codec.http2.Http2ConnectionHandler$FrameDecoder.decode(Http2ConnectionHandler.java:378)
at io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:438)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508)
...
}
It seems that with Vert.x 4.0.3, the parsing of the HTTP/2 headers fail so the status default to UNKNOWN
. So of course that has impacts when we have to take decision based on that Exception
's Status
.
After a few tries with forcing different versions of Netty, I found that this regression is coming in Netty 4.1.60.Final.
With Vert.x 4.0.3 but forcing Netty to 4.1.59.Final it is working fine.
Find attached the small test I use to emphasize this regression, it is fully based on the gRPC example you documented on : https://vertx.io/docs/vertx-grpc/java/ . See the class Client
.
vertx-grpc-example.zip