Closed yhirose closed 4 years ago
As a temporary measure, developers can implement SSE by generating events with their own callback in Response.content_producer
because SSE protocol is simply implemented on top of HTTP chunked transfer protocol, which is already supported by cpp-httplib.
For example, check my gist here.
Moved the following comment from #206:
SSE in W3 specs is implemented on top of chunked transfer and uses the
text/event-stream
content type. Groups of events are seperated using a sequence of two line terminators. For the specific case of servers sending a content type oftext/event-stream
, clients are not expected to interpret double line terminators as end of content. In fact, there is no such thing as "end of content".However,
ContentReceiver
is unable to properly handle SSE because it always immediately closes connection when it receives two line terminators.
by inspecting Chrome network activity while visiting sites that test SSE capability, I have noticed that some servers which implement SSE will terminate the chunked response after the first chunk (but not the connection) and expect Chrome to send another request using the same connection to resume the event stream.
I suspect that those servers are doing this so that they can "force" web browsers to essentially "ping" the server to show it is still interested in the events.
Since the developers of Chrome appear to be fine with re-sending requests whenever the chunked response is terminated, I think that it should be fine to implement SSE client-side by simply using a loop which re-sends requests on a keep-alive connection, unless the server responds with a non-2xx status, or times out (based on the retry: <milliseconds>
directive in SSE messages).
This would require cpp-httplib to allow potentially infinite keep-alive requests, which is not currently supported at the moment.
I have updated the github gist with a new demo to show how to make an SSE client. However, I am not sure if cpp-httplib will re-use connections automatically.
Batching client requests like you currently support is a pretty good idea, but it can't really be used for the cases where requests must be generated on the fly or the number of requests to be made is not known in advance. If cpp-httplib client currently does not re-use connections automatically, I might suggest to give a downstream developer the ability to manage connection reuse by giving the option to retrieve a "connection" reference/object and pass it to another request as a parameter or otherwise.
Added a SSE example on server. https://github.com/yhirose/cpp-httplib/blob/master/example/ssesvr.cc
Added a SSE client example. https://github.com/yhirose/cpp-httplib/blob/master/example/ssecli.cc
This is the same as curl http://localhost:1234/event1 -N
.
Added a SSE example on server. https://github.com/yhirose/cpp-httplib/blob/master/example/ssesvr.cc
This example is not complete to show a sse implementation because the main thread didn't take request. usually the api method (GET/POST) will generate response according to request, so the send_event() will be called in GET handler, then the wait_event will not get executed. (I tried in my server). i'm a bit new to httplib, could anyone help to take a look at my case? Thanks!
@gryffindor-rr You appear to have confused the usage of send_event()
and wait_event()
with each other. In the example, send_event()
is called from a dedicated worker thread entirely separate from httplib, not in the GET handler, and not in the main thread.
When listen
is called, httplib will occupy the current thread with its internal loop and wait for incoming requests, in this case the main thread. Httplib will also internally create its own separate pool of threads, to which jobs are dispatched to process incoming requests and generate responses.
set_chunked_content_provider
allows you to provide a callback function. The callback doesn't execute immediately, but only after the end of the GET handler. The callback is executed repeatedly for each connection until the connection is closed, or until false
is returned. Note that because httplib doesn't implement coroutines or async processing, callbacks will occupy a thread in the pool even if they are simply waiting.
See #106