Hello,

I am trying to implement a HTTP2 server in netty, only h2c is needed, no 
TLS. The code is based on the sample found in 
io.netty.example.http2.helloworld.server. Right now, I am struggling with 
how to correctly send back a response asynchronously from when a request 
was received in a Http2FrameListener. See my current implementation at:

https://github.com/rrva/netty-http2/blob/main/src/main/kotlin/main.kt

it is a small hello world server. For GET requests, I read the request 
in Http2FrameListener.onHeadersRead and simulate that a response is 
computed asynchronously by some other thread pool than the netty event 
loop, and when the response CompletableFuture completes I want to send the 
response back in the correct context. Here I tried to execute the sending 
on the response on the same executor as the context event loop. The 
implementation I linked above is obviously not correct. With this 
implementation, I sometimes get a correct response sent back to the client 
and sometimes only headers are sent back but no data frame. I assume this 
is because I am using HTTP2FrameListener and the context in the wrong way 
and the frame encoder encounters some race condition or similar. In the 
code, if the request path is /graphql then I try this async response 
handling, but on other paths like / the response is sent back directly and 
that works fine.

 How would a better implementation look like if I still want to execute the 
computation of the response in another thread pool?

to send responses I use:

private fun sendResponse(
ctx: ChannelHandlerContext,
streamId: Int,
payload: ByteBuf,
status: HttpResponseStatus = HttpResponseStatus.OK
) {
val headers = DefaultHttp2Headers().status(status.codeAsText())
encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise())
encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise())
ctx.flush()
}

in Http2FrameListener.onHeadersRead I have a graphResponseFuture which is 
the future for my response which I then invoke sending the response on like 
this (see github link above for full code):

graphResponseFuture.whenCompleteAsync({ graphResponse, throwable ->
if (throwable != null) {
log.error("Error processing data", throwable)
val errorPayload = ctx.alloc().buffer().writeBytes("Internal server error".
toByteArray())
sendResponse(ctx, streamId, errorPayload, HttpResponseStatus.
INTERNAL_SERVER_ERROR)
} else {
val responseBuffer = ctx.alloc().buffer()
serializeGraphResponseToByteBuf(graphResponse, responseBuffer)
sendResponse(ctx, streamId, responseBuffer)
}
}, ctx.channel().eventLoop())

When testing, I reproduce problems by repeatedly requesting like this:

curl -v --http2-prior-knowledge "http://localhost:9093/graphql";

but I never see issues with

curl -v --http2-prior-knowledge "http://localhost:9093/";

the difference being that in the second request the response is sent 
synchronously when the request handler is invoked.

-- 
You received this message because you are subscribed to the Google Groups 
"Netty discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/netty/ead5cf45-c78c-4965-8c73-ea7b1b253b35n%40googlegroups.com.

Reply via email to