Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement request: support for Server Sent Events aka EventSource #7008

Closed
ewaldc opened this issue Jan 10, 2020 · 7 comments · Fixed by #7012
Closed

Enhancement request: support for Server Sent Events aka EventSource #7008

ewaldc opened this issue Jan 10, 2020 · 7 comments · Fixed by #7012

Comments

@ewaldc
Copy link
Contributor

ewaldc commented Jan 10, 2020

This is a request for support of HTML5 Server Sent Events aka Event Source.
It allows e.g. to implement an event bus over HTML.

The required changes to the ESP8266WebServer library are absolutely minimal: zero impact on existing code and increase in code size/memory footprint of just few bytes (basically sizeof bool).

Essentially, the variable bool keepCurrentClient which is currently hardcoded to false, becomes a protected class variable in the ESP8266WebServer class that can be modified via a new function called keepCurrentClient(bool).

To enable Server Sent Events/event subscription, it requires two web server instances, one being changed to keep the client socket/session (an event listener) open until disconnected.

ESP8266WebServer _server(port);
ESP8266WebServer _SSEserver(port + 1);
_server.begin();
_SSEserver.keepCurrentClient(true);
_SSEserver.begin();

Everything else can implemented in user code (event subscription, event bus keepalive messages, event payload etc.).
I have this currently working on a clean fork from master branch with just this one change.
Happy to post a PR (it's ready to post), if this is a reasonable request.
I can also contribute a working example, I suppose in the ESP8266WebServer examples section.

Thanks once again for this brilliant library and for taking the time to consider this request,
Ewald

@d-a-v
Copy link
Collaborator

d-a-v commented Jan 10, 2020

Thanks !
Please post your PR and its example

ewaldc added a commit to ewaldc/Arduino that referenced this issue Jan 12, 2020
Illustrates the use of SSE using ESP8266WebServer
@ewaldc
Copy link
Contributor Author

ewaldc commented Jan 12, 2020

@d-a-v,
Example submitted. It turns out that there is no need for the keepCurrentClient enhancement.
I found another way to make it work:

  1. set the contentLength to CONTENT_LENGTH_UNKNOWN which allows to submit a virtually endless payload (needed for an event bus)
  2. add a KeepAlive timer, using the Ticker library, which keeps the socket open by sending e.g. every minute a keep-alive message in case there are no (normal) state update events. The use of a KeepAlive timer is not a violation of the with SSE - EventSource standard, it just requires the client to handle/ignore the KeepAlive events.

Hence, no need for the enhancement request. Sorry for that, but I hope the example is still useful. I tried for days to make it work using WebSockets and AsyncWebServer but both solutions are much more complex, consume much more memory and could not be made stable.

The one thing I have not figured out is why either of client.setNoDelay(true); or client.setSync(true); (lines 121/122 in the example) cause a WDT Soft Reset in the KeepAlive function when issuing client.println(), while the regular state event updates using a similar client.printf_P() continues to run without any problem! They both write to the same WiFiClient with IMHO very similar calls.
Setting "no delay" would ideally make the event bus a bit more real time. Adding yield() calls makes no difference, as expected as there is no blocking user code.
Any idea why this would cause a WDT Soft Reset ?

Hope the example is still useful.

@d-a-v
Copy link
Collaborator

d-a-v commented Jan 17, 2020

I think you should try to use ticker.attach_scheduled() instead of ticker.attach() so you can do whatever is needed in the callback (otherwise the callback is treated like an ISR that is to be used with more constraints).

@ewaldc
Copy link
Contributor Author

ewaldc commented Jan 18, 2020

Thanks for the suggestion, this was already top of mind as it is not recommended to do networking stuff in Ticker.attach(), even though a "keepalive" packet is less than 100 bytes. Rather than posting a PR I will post the update code a link to e.g. pastebin.

@d-a-v
Copy link
Collaborator

d-a-v commented Jan 18, 2020

Just commit changes to your branch, it will update the PR

@ewaldc
Copy link
Contributor Author

ewaldc commented Jan 20, 2020

Thanks for your flexibility, I have posted the updated example here
What's new:

  • code simplified and reduced in # of lines, yet more functional
  • removed second web server
  • every event subscription now has its own Ticker for keep-alive events
  • moved to Ticker.attach_scheduled(), which allows for network operation within the callback
  • Eventsource socket now set to setNoDelay(true) and setSync(true) (no more crashes)
  • multiple subscriptions and listeners allowed (also for each client IP)
  • introduction of listener channels
  • documentation improved (within code)
  • validated with lasted master branch
    Essentially the same code within a much larger application has passed 48 hours of stress test

@ewaldc
Copy link
Contributor Author

ewaldc commented Feb 4, 2020

Example updated after feedback/review from users

earlephilhower added a commit that referenced this issue May 16, 2020
* Server Sent Events example - issue #7008

Illustrates the use of SSE using ESP8266WebServer

* Update ServerSentEvents.ino

* Create ServerSentEventsMultiClient.ino

* sync

* Update ServerSentEvents.ino

* Update ServerSentEvents.ino

Fix  missing variables in printf statments
Fix subscriptioncount not decreasing
Fix SSEBroadcastState (argument sequence wrong)

* Undo the library additions, move to current master

* Fix compiler warning

* Address review and fix multi-sensor updates

Address points of @devyte's code review:
* Use IPAddress vs. uint32_t
* Refactor the URL parsing logic to use strlen vs. sizeof, since there
  was some confusion in the original (correct) version
* Minimize copies of WiFiClients while in use
* Use byref access for sensor updates

Fix multi-sensor updates
* Create an update Ticker for each sensor, because the original code
  only had one whose callback was overridden by sensorB, meaning sensorA
  never changed

* Fix IPv6 build errors

* Remove WiFiClient extraneous copy

Avoid duplicating WiFiClient by using the WiFiClient object embedded in
the subscriber[] array instead.

Co-authored-by: Earle F. Philhower, III <earlephilhower@yahoo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants