When the Server Has News
Two Ways to Fake a Push
Polling every two seconds left the scoreboard about a second late and spent 1,800 requests an hour per viewer, nearly all of them answered with “nothing new”. Both costs come from the same place: the server has the news, but it may only answer when a request is waiting. There are two well-known ways to stretch
Hold the request open
The first trick is long polling. The viewer sends a request as before, but the server does not answer at once. It holds the request open until a score changes, or until its own wait runs out, for example after 25 seconds. Then it answers, and the viewer sends the next request immediately.
client server
GET /scores ---->
holds the request
a score changes
<---- 200 OK, the new scores
GET /scores ---->
holds the request
25 seconds pass, no change
<---- 200 OK, nothing new
GET /scores ---->
holds the request
The viewer asks, and the server sits on the request. When a score changes, the server answers right away with the new scores, and the viewer asks again. When 25 quiet seconds pass, the server answers that nothing changed, and the viewer asks again.
The lateness almost disappears. A change no longer waits for the next poll, because a request is nearly always already waiting at the server; it usually waits only for the answer to cross the network. It waits a little longer when it lands while the previous answer is still on its way and the next request has not reached the server yet. The empty answers shrink too: one per 25 quiet seconds instead of one every 2 seconds.
There is one new rule for the client. In HTTP with Python you gave every request a timeout, and you learned that a timeout is your client deciding to stop waiting, not the server failing. A long poll is a request that is supposed to wait, so the client’s timeout must be longer than the server’s hold. Otherwise the client gives up on every quiet period just before the server’s honest “nothing new” arrives.
The server holds a long poll for up to 25 seconds. What client timeout makes sense?
One response that never ends
The second trick goes further: one request, and a response that never finishes. This is Server-Sent Events, often shortened to SSE. The viewer sends one GET, and the server answers with a head whose Content-Type is text/event-stream:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Transfer-Encoding: chunked
The head has no Content-Length, because the server does not know how long the body will be. It sends the body in chunks, one of the three ways a body can end that you met in HTTP with Python, and it keeps the connection open. Whenever a score changes, the server writes one more event into the body. Once the chunk sizes are taken out, the body the viewer reads looks like this:
data: Owls 12
data: Herons 9
Each event is a data: line followed by an empty line. The empty line is what ends an event; an event may have several data: lines before it. The data here is plain text, but it can be anything that fits on a line,
Reading events out of that text takes the tools you already have. This block works on the same text, written as one
It prints Owls 12 and then Herons 9. Splitting at "\n\n", the empty line between events, gives one piece per event, plus an empty string after the last one, because the stream ends with an empty line. The data: , which includes that empty string, and the event[6:] drops the six characters of data: from the front.
A browser has this reading built in, and it also reopens a stream that drops. On the server side, an event stream is an ordinary HTTP response, so it needs nothing that HTTP does not already have.
In an event stream, what ends one event?
What each one keeps
Each trick removes one cost of polling and keeps one limit.
| Technique | What it removes | What it keeps |
|---|---|---|
| Long polling | the wait for the next poll, and most empty answers | one request for every piece of news, and the client still starts every exchange |
| Server-Sent Events | the request for every piece of news: one response carries them all | one direction only, from server to client |
With long polling, the server can only answer a request, so every score change still ends one request and needs the viewer to send another. With SSE, the server writes whenever it likes, but only the server writes. The viewer cannot send anything back on the event stream.
That matters for the scoreboard, because the scorekeeper also has something to say. With SSE, a viewer receives scores on the stream, but the scorekeeper’s new score has to travel some other way.
With Server-Sent Events, how does the scorekeeper send a new score to the server?
So the pieces exist: a request the server can hold, and a response that keeps carrying news. What is missing is one connection on which both sides may speak whenever they want. The next lesson