0
votes

I'm trying to build real time notification system for users using Server sent events in python.

The issue I'm facing is when I refresh the browser, which means the browser then will try to hit the EventSource url again, and as per docs it should send the event.lastEventId as part of headers in next request. I'm getting None every time I refresh the page.

<!DOCTYPE html>
<html>
    <header><title>SSE test</title></header>
    <body>
        <ul id="list"></ul>
        <script>
        const evtSource = new EventSource("/streams/events?token=abc");
        evtSource.onmessage = function(event) {
            console.log('event', event)
            console.log('event.lastEventId', event.lastEventId)
            const newElement = document.createElement("li");
            const eventList = document.getElementById("list");
            newElement.innerHTML = "message: " + event.data;
            eventList.appendChild(newElement);
        }
        </script>
    </body>
</html>

On the server side

from sse_starlette.sse import EventSourceResponse
from asyncio.queues import Queue
from starlette.requests import Request

@event_router.get(
"/streams/events",
status_code=HTTP_200_OK,
summary='',
description='',
response_description='')
async def event_stream(request: Request):
    return EventSourceResponse(send_events(request))


async def send_events(request: Request):
    try:
        key = request.query_params.get('token')
        last_id = request.headers.get('last-event-id')
        print('last_id ', last_id) # this value is always None

        connection = Queue()
        connections[key] = connection

        while RUNNING:
            next_event = await connection.get()
            print('event', next_event)
            yield dict(data=next_event, id=next_event['id'])
            connection.task_done()
    except asyncio.CancelledError as error:
        pass

Now, as per every doc on SSE, when client reconnects or refreshes the page it will send the last-event-id in headers. I'm trying to read it using request.headers.get('last-event-id'), but this is always null.

Any pointers on how to get the last event id would be helpful. Also, how would I make sure that I do't send the same events even later once the user has seen the events, as my entire logic would be based on last-event-id received by the server, so if its None after reading events with Id 1 to 4, how would I make sure in server that I should not send these back even if last-event-id is null for the user

.

Adding browser snaps

1st pic shows that the events are getting received by the browser. e.g. {alpha: abc, id:4}

2nd pic shows that the event received is setting the lastEventId correctly.

enter image description here

enter image description here

1

1 Answers

1
votes

I think this is a wrong understanding. Where did you get the part that says "when client reconnects or refreshes the page it will send the last-event-id in headers".

My understanding is that the last ID is sent on a reconnect. When you refresh the page directly, that is not seen as a broken connection and a re-connect. You are completely re-starting the whole page and forming a brand new SSE connection to the server from the browser. Keep in mind if your SSE had a certain request parameter for example and this could be driven by something on the page. You could have two tabs open from the same "page" but put different items into the page which causes this request parameter to be different in your SSE. They are two different SSE connections altogether. One does not affect the other except that you can reach the maximum connection limit of a browser if you have too many. In the same way, if you click refresh, this is like a new tab being created. It is a totally new connection.