Skip to content

Commit 28ae6e5

Browse files
committed
Added SSEResponse class
1 parent 90085c3 commit 28ae6e5

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

adafruit_httpserver/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
ChunkedResponse,
5959
JSONResponse,
6060
Redirect,
61+
SSEResponse,
6162
)
6263
from .route import Route
6364
from .server import Server

adafruit_httpserver/response.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,97 @@ def _send(self) -> None:
403403
self._send_headers()
404404
self._close_connection()
405405

406+
407+
class SSEResponse(Response): # pylint: disable=too-few-public-methods
408+
"""
409+
Specialized version of `Response` class for sending Server-Sent Events.
410+
411+
Allows one way communication with the client using a persistent connection.
412+
413+
Keep in mind, that in order to send events, the socket must be kept open. This means that you
414+
have to store the response object somewhere, so you can send events to it and close it later.
415+
416+
**It is very important to close the connection manually, it will not be done automatically.**
417+
418+
Example::
419+
420+
sse = None
421+
422+
@server.route(path, method)
423+
def route_func(request: Request):
424+
425+
# Store the response object somewhere in global scope
426+
global sse
427+
sse = SSEResponse(request)
428+
429+
return sse
430+
431+
...
432+
433+
# Later, when you want to send an event
434+
sse.send_event("Simple message")
435+
sse.send_event("Message", event="event_name", id=1, retry=5000)
436+
437+
# Close the connection
438+
sse.close()
439+
"""
440+
441+
def __init__( # pylint: disable=too-many-arguments
442+
self,
443+
request: Request,
444+
headers: Union[Headers, Dict[str, str]] = None,
445+
) -> None:
446+
"""
447+
:param Request request: Request object
448+
:param Headers headers: Headers to be sent with the response.
449+
"""
450+
super().__init__(
451+
request=request,
452+
headers=headers,
453+
content_type="text/event-stream",
454+
)
455+
self._headers.setdefault("Cache-Control", "no-cache")
456+
self._headers.setdefault("Connection", "keep-alive")
457+
458+
def _send(self) -> None:
459+
self._send_headers()
460+
461+
def send_event( # pylint: disable=too-many-arguments
462+
self,
463+
data: str,
464+
event: str = None,
465+
id: int = None, # pylint: disable=redefined-builtin,invalid-name
466+
retry: int = None,
467+
custom_fields: Dict[str, str] = None,
468+
) -> None:
469+
"""
470+
Send event to the client.
471+
472+
:param str data: The data to be sent.
473+
:param str event: (Optional) The name of the event.
474+
:param int id: (Optional) The event ID.
475+
:param int retry: (Optional) The time (in milliseconds) to wait before retrying the event.
476+
:param Dict[str, str] custom_fields: (Optional) Custom fields to be sent with the event.
477+
"""
478+
message = f"data: {data}\n"
479+
if event:
480+
message += f"event: {event}\n"
481+
if id:
482+
message += f"id: {id}\n"
483+
if retry:
484+
message += f"retry: {retry}\n"
485+
if custom_fields:
486+
for field, value in custom_fields.items():
487+
message += f"{field}: {value}\n"
488+
message += "\n"
489+
490+
self._send_bytes(self._request.connection, message.encode("utf-8"))
491+
492+
def close(self):
493+
"""
494+
Close the connection.
495+
496+
**Always call this method when you are done sending events.**
497+
"""
498+
self._send_bytes(self._request.connection, b"event: close\n")
499+
self._close_connection()

0 commit comments

Comments
 (0)