karlseguin / http.zig

An HTTP/1.1 server for zig
MIT License
533 stars 41 forks source link

Small changes to support SSE event-stream #5

Closed zigster64 closed 1 year ago

zigster64 commented 1 year ago

From https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

Must admit, first I have ever heard of this !

This is a dead simple protocol that allows a normal GET endpoint to persist, and be used to push messages from the server to the client over a standard HTTP/1 connection. Good browser support (ALL), and polyfills do exist for older browsers to use the API as well.

To get http.zig working with this, just needs to set the .ContentType to "text/event-stream" AND .. not send the content-length, otherwise the client closes the connection. AND ... not use chunked encoding.

Then the handler just sits in a loop, and produces message frames on demand. Since we are running handlers in threads, this makes it trivial to use std.Thread.Condition to signal / broadcast event-stream handlers to do updates as soon as some state changes. Proper realtime updates over HTTP, no need for websockets !

You can see some small changes I made in the "event-source" branch on my fork to get it all working. https://github.com/zigster64/http.zig/tree/event-source

Main points :

Currently hacking with a demo app that uses event-streams to do realtime updates. It's a very cool API, really easy to apply.

https://github.com/zigster64/zchat

Here is what an event-stream handler using http.zig looks like https://github.com/zigster64/zchat/blob/main/src/state.zig#L80-112

PS - had to do a number of other changes to get inline with latest zig, but I see you have been doing the exact same changes in yours at the same time ! I used zig fmt to auto fix my fork to be inline with the latest zig, but that has made some radical changes to the spacing compared to your master ... so Im reluctant to simply create a PR based on my fork yet. Dunno - is it worth the effort to sync formatting an create a PR do you think ?

karlseguin commented 1 year ago

Thanks, I implemented the logic.

It's mostly the same as what you did, but I opted for res.startEventStream() to (a) internally call "write" and (b) return res.stream:

const stream = res.startEventStream();
// no need to call res.write()

While the returned stream is just res.stream, I think this API better illustrates that res should not be used after startEvenStream() is called.

zigster64 commented 1 year ago

Nice work, that saves some potential footguns.

will port zchat to use master, and try and sort out what’s up with my formatting diffs too.

next up, I’m going to look at jwt’s over the next couple of weeks.