I'm using nghttp2 to implement a REST server which should use HTTP/2 and server-sent events (to be consumed by an EventSource in the browser). However, based on the examples it is unclear to me how to implement SSE. Using res.push() as in asio-sv.cc
doesn't seem to be the right approach.
What would be the right way to do it? I'd prefer to use nghttp2's C++ API, but the C API would do as well.
Yup, I did something like that back in 2018. The documentation was rather sparse :).
First of all, ignore
response::push
because that's the HTTP2 push -- something for proactively sending unsolicited objects to the client before it requests them. I know it sounds like what you need, but it is not -- the typical use case would be proactively sending a CSS file and some images along with the originally requested HTML page.The key thing is that your
end()
callback must eventually returnNGHTTP2_ERR_DEFERRED
whenever you run out of data to send. When your application somehow obtains more data to be sent, callhttp::response::resume()
.Here's a simple code. Build it as
g++ -std=c++17 -Wall -O3 -ggdb clock.cpp -lssl -lcrypto -pthread -lnghttp2_asio -lspdlog -lfmt
. Be careful, modern browsers don't do HTTP/2 over a plaintext socket, so you'll need to reverse-proxy it via something likenghttpx -f '*,8080;no-tls' -b '::1,10080;;proto=h2'
.I have a feeling that my queue handling is probably too complex. When testing via
curl
, I never seem to run out of buffer space. In other words, even if the client is not reading any data from the socket, the library keep invokingsend_chunk
, asking for up to 16kB of data at a time for me. Strange. I have no idea how it works when pushing more data more heavily.My "real code" used to have a third state,
Closed
, but I think that blocking events viaon_close
is enough here. However, I think you never want to entersend_chunk
if the client has already disconnected, but before the destructor gets called.