You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,6 +56,15 @@ HTTP.open(:GET, "https://tinyurl.com/bach-cello-suite-1-ogg") do http
56
56
end
57
57
```
58
58
59
+
Handle [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) (SSE) streams by passing an `sse_callback` function to `HTTP.request`:
Copy file name to clipboardExpand all lines: docs/src/client.md
+28Lines changed: 28 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -233,6 +233,34 @@ end -> HTTP.Response
233
233
234
234
Where the `io` argument provided to the function body is an `HTTP.Stream` object, a custom `IO` that represents an open connection that is ready to be written to in order to send the request body, and/or read from to receive the response body. Note that `startread(io)` should be called before calling `readavailable` to ensure the response status line and headers are received and parsed appropriately. Calling `eof(io)` will return true until the response body has been completely received. Note that the returned `HTTP.Response` from `HTTP.open` will _not_ have a `.body` field since the body was read in the function body.
235
235
236
+
### Server-Sent Events
237
+
238
+
HTTP.jl can parse [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) streams directly via the `sse_callback` keyword on `HTTP.request`. When this keyword is supplied, HTTP.jl incrementally parses the incoming bytes as an event stream and invokes the callback with an `HTTP.SSEEvent` struct for every event:
The callback can be `f(event)` or `f(stream, event)`. The two-argument form allows cancelling the request by calling `close(stream)` (for example, in response to a specific event).
251
+
252
+
Each callback receives a `SSEEvent` with the following fields:
253
+
254
+
-`data::String`: newline-joined `data:` payload for the event (with the trailing newline removed).
255
+
-`event::Union{Nothing,String}`: the most recent `event:` field, or `nothing` when not provided (equivalent to the default `"message"` type).
256
+
-`id::Union{Nothing,String}`: the last `id:` value observed, automatically persisted between events per the SSE specification.
257
+
-`retry::Union{Nothing,Int}`: the last `retry:` directive in milliseconds, propagated to subsequent events until another `retry:` value is parsed.
258
+
-`fields::Dict{String,String}`: newline-joined string values for every field encountered since the previous event, including custom non-standard fields.
259
+
260
+
Because HTTP.jl streams the response directly to the callback, the returned `HTTP.Response` will always have `response.body === HTTP.nobody`. The `sse_callback` keyword cannot be combined with `response_stream` or a custom `iofunction`, and parsing errors raise `HTTP.SSEError`. Compressed streams are supported automatically unless `decompress=false` is explicitly set.
261
+
262
+
For a full end-to-end example, see [`docs/examples/server_sent_events.jl`](https://github.com/JuliaWeb/HTTP.jl/blob/master/docs/examples/server_sent_events.jl).
263
+
236
264
### Download
237
265
238
266
A [`download`](@ref) function is provided for similar functionality to `Downloads.download`.
Copy file name to clipboardExpand all lines: docs/src/server.md
+77-1Lines changed: 77 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -108,8 +108,84 @@ Lower-level core server functionality that only operates on `HTTP.Stream`. Provi
108
108
109
109
Nginx-style log formatting is supported via the [`HTTP.@logfmt_str`](@ref) macro and can be passed via the `access_log` keyword argument for [`HTTP.listen`](@ref) or [`HTTP.serve`](@ref).
110
110
111
+
## Server-Sent Events (SSE)
112
+
113
+
HTTP.jl provides built-in support for [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events), a standard for pushing real-time updates from server to client over HTTP.
114
+
115
+
### Creating an SSE Response
116
+
117
+
Use [`HTTP.sse_stream`](@ref) to create an SSE stream from a response object:
118
+
119
+
```julia
120
+
using HTTP
121
+
122
+
HTTP.serve() do request
123
+
response = HTTP.Response(200)
124
+
stream = HTTP.sse_stream(response)
125
+
126
+
# Spawn a task to write events asynchronously
127
+
Threads.@spawnbegin
128
+
try
129
+
for i in1:5
130
+
write(stream, HTTP.SSEEvent("Event $i"))
131
+
sleep(1)
132
+
end
133
+
finally
134
+
close(stream)
135
+
end
136
+
end
137
+
138
+
return response
139
+
end
140
+
```
141
+
142
+
The `sse_stream` function:
143
+
1. Creates an `SSEStream` for writing events
144
+
2. Sets the response body to the stream
145
+
3. Adds required headers: `Content-Type: text/event-stream` and `Cache-Control: no-cache`
146
+
4. Uses a bounded internal buffer (configurable via `max_len`, default 16 MiB) to provide backpressure if the client is slow to read
147
+
148
+
### Writing Events
149
+
150
+
Write events using `write(stream, HTTP.SSEEvent(...))`:
151
+
152
+
```julia
153
+
# Simple data-only event
154
+
write(stream, HTTP.SSEEvent("Hello, world!"))
155
+
156
+
# Event with type (for client-side addEventListener)
-`event::Union{Nothing,String}`: Event type name (maps to `addEventListener` on client)
177
+
-`id::Union{Nothing,String}`: Event ID for reconnection tracking
178
+
-`retry::Union{Nothing,Int}`: Suggested reconnection delay in milliseconds
179
+
180
+
### Important Notes
181
+
182
+
- Always close the stream when done: `close(stream)`
183
+
- Use `Threads.@spawn` or `@async` to write events without blocking the handler
184
+
- The handler must return the response immediately; events are written asynchronously
185
+
- For client-side SSE consumption, see the [Client documentation](client.md#Server-Sent-Events)
186
+
111
187
## Serving on the interactive thead pool
112
188
113
189
Beginning in Julia 1.9, the main server loop is spawned on the [interactive threadpool](https://docs.julialang.org/en/v1.9/manual/multi-threading/#man-threadpools) by default. If users do a Threads.@spawn from a handler, those threaded tasks should run elsewhere and not in the interactive threadpool, keeping the web server responsive.
114
190
115
-
Note that just having a reserved interactive thread doesn’t guarantee CPU cycles, so users need to properly configure their running Julia session appropriately (i.e. ensuring non-interactive threads available to run tasks, etc).
191
+
Note that just having a reserved interactive thread doesn’t guarantee CPU cycles, so users need to properly configure their running Julia session appropriately (i.e. ensuring non-interactive threads available to run tasks, etc).
0 commit comments