ocaml http/1, http/2 and websocket client and server library
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: fix streaming response documentation

Replaced incorrect Body_stream example with accurate Server.respond_stream
API explanation:
- Explained next function signature and behavior
- Added working examples for basic streaming and file streaming
- Documented content_length vs chunked encoding

+78 -12
+78 -12
docs/guides/responses.md
··· 146 146 147 147 ## Streaming Responses 148 148 149 - For large responses, use streaming: 149 + For large or dynamically generated responses, use `Server.respond_stream`. This avoids buffering the entire response in memory. 150 + 151 + ### How Streaming Works 152 + 153 + The streaming API uses a `next` function that the server calls repeatedly: 150 154 151 155 ```ocaml 152 - let handler req = 153 - { 154 - status = `OK; 155 - headers = [("Content-Type", "text/plain")]; 156 - response_body = Body_stream (fun write flush -> 157 - for i = 1 to 100 do 158 - write (Printf.sprintf "Line %d\n" i); 159 - if i mod 10 = 0 then flush () 160 - done 161 - ); 162 - } 156 + Server.respond_stream : 157 + ?status:status -> 158 + ?headers:(string * string) list -> 159 + ?content_length:int64 -> 160 + (unit -> Cstruct.t option) -> (* The next function *) 161 + response 163 162 ``` 163 + 164 + The `next` function: 165 + - Returns `Some chunk` to send that chunk to the client 166 + - Returns `None` to signal end of stream and close the response 167 + - Is called repeatedly until it returns `None` 168 + 169 + The server flushes after each chunk, so data is sent immediately. 170 + 171 + ### Basic Example 172 + 173 + Stream 100 lines of text: 174 + 175 + ```ocaml 176 + let handler _req = 177 + let counter = ref 0 in 178 + let next () = 179 + incr counter; 180 + if !counter <= 100 then 181 + let line = Printf.sprintf "Line %d\n" !counter in 182 + Some (Cstruct.of_string line) 183 + else 184 + None (* End of stream *) 185 + in 186 + Server.respond_stream 187 + ~headers:[("Content-Type", "text/plain")] 188 + next 189 + ``` 190 + 191 + ### Streaming from a File 192 + 193 + ```ocaml 194 + let stream_file path = 195 + let ic = open_in path in 196 + let buf = Bytes.create 4096 in 197 + let next () = 198 + match input ic buf 0 4096 with 199 + | 0 -> close_in ic; None 200 + | n -> Some (Cstruct.of_bytes ~off:0 ~len:n buf) 201 + | exception End_of_file -> close_in ic; None 202 + in 203 + Server.respond_stream next 204 + ``` 205 + 206 + ### With Known Content-Length 207 + 208 + If you know the total size, provide `content_length` to avoid chunked encoding: 209 + 210 + ```ocaml 211 + let stream_known_size data_size next = 212 + Server.respond_stream 213 + ~content_length:(Int64.of_int data_size) 214 + next 215 + ``` 216 + 217 + ### Chunked vs Fixed-Length 218 + 219 + | Scenario | Behavior | 220 + |----------|----------| 221 + | `content_length` provided | Server sends `Content-Length` header, fixed-length body | 222 + | No `content_length` | Server uses `Transfer-Encoding: chunked` | 223 + 224 + ### Use Cases 225 + 226 + - **Large files**: Stream without loading entire file into memory 227 + - **Generated content**: Stream as you generate (reports, exports) 228 + - **Server-Sent Events**: Stream events over time (see [SSE docs](../real-time/sse.md)) 229 + - **Slow data sources**: Stream as data becomes available 164 230 165 231 ## JSON Responses 166 232