AtlatestRepositorysigil-http
1# HTTP
2
3> HTTP/1.1 client and server functionality.
4
5```scheme
6(import (sigil http))
7```
8
9## Client
11### http-get
13Make an HTTP GET request.
15```scheme
16(let ((response (http-get "https://example.com/")))
17 (display (http-response-body response)))
19;; With custom headers
20(http-get "https://api.example.com/data"
21 '(("Authorization" . "Bearer token123")))
22```
24### http-post
26Make an HTTP POST request.
28```scheme
29;; Form data (default Content-Type: application/x-www-form-urlencoded)
30(http-post "https://example.com/login"
31 "username=alice&password=secret")
33;; JSON body
34(import (sigil json))
35(http-post "https://api.example.com/users"
36 (json-encode #{ name: "Alice" })
37 '(("Content-Type" . "application/json")))
38```
40### http-put / http-delete
42```scheme
43(http-put "https://api.example.com/users/1"
44 (json-encode #{ name: "Bob" })
45 '(("Content-Type" . "application/json")))
47(http-delete "https://api.example.com/users/1")
48```
50### http-request
52Low-level request function for any HTTP method.
54```scheme
55(http-request 'PATCH "https://api.example.com/resource"
56 '(("Content-Type" . "application/json"))
57 (json-encode #{ status: "active" }))
58```
60### Response Accessors
62```scheme
63(let ((response (http-get "https://example.com/")))
64 (http-response-status response) ; => 200
65 (http-response-headers response) ; => #{ content-type: "text/html" ... }
66 (http-response-body response)) ; => "<html>..."
67```
69### URL Parsing
71```scheme
72(let ((url (parse-url "https://example.com:8080/path?query=1")))
73 (url-scheme url) ; => "https"
74 (url-host url) ; => "example.com"
75 (url-port url) ; => 8080
76 (url-path url) ; => "/path"
77 (url-query url)) ; => "query=1"
78```
80## Server
82### http-serve
84Start a blocking HTTP server.
86```scheme
87(http-serve 8080
88 (lambda (request)
89 (http-response/html 200 "<h1>Hello!</h1>")))
90```
92### Request Accessors
94```scheme
95(lambda (req)
96 (http-request-method req) ; => 'GET, 'POST, etc.
97 (http-request-path req) ; => "/users/123"
98 (http-request-query req) ; => "format=json" or #f
99 (http-request-headers req) ; => #{ content-type: "..." ... }
100 (http-request-body req) ; => string or #f
101 (http-request-header req content-type:)) ; => "application/json"
102```
104### Response Constructors
106```scheme
107;; Plain text
108(http-response/text 200 "Hello, World!")
110;; HTML
111(http-response/html 200 "<h1>Welcome</h1>")
113;; JSON (auto-encodes)
114(http-response/json 200 #{ status: "ok" users: #[1 2 3] })
116;; Redirect
117(http-response/redirect "/new-location")
118(http-response/redirect "/new-location" 301) ; Permanent
120;; Error
121(http-response/error 500 "Internal Server Error")
123;; Not Found
124(http-response/not-found)
125```
127### Custom Response
129```scheme
130(http-response
131 status: 201
132 headers: #{ content-type: "application/json"
133 x-custom-header: "value" }
134 body: (json-encode #{ id: 123 }))
135```
137### File Serving and Range Requests
139`http-response/file` streams a file from disk with an auto-detected MIME type.
140Pass `range:` the request's raw `Range` header to honor byte ranges — media
141seeking and resumable downloads:
143```scheme
144;; Whole file (200 OK), advertises Accept-Ranges: bytes
145(http-response/file "/srv/video.mp4")
147;; Honor the request Range header: 206 Partial Content for a satisfiable
148;; range, 416 Range Not Satisfiable otherwise, 200 when there is no Range.
149(http-response/file "/srv/video.mp4"
150 range: (http-request-header req "Range"))
151```
153Supported range forms: `bytes=A-B` (first–last), `bytes=A-` (open-ended), and
154`bytes=-N` (the last N bytes). A `206` response carries `Content-Range` and the
155exact `Content-Length`; a `416` carries `Content-Range: bytes */<total>`.
157### Persistent Connections (keep-alive)
159Non-streaming HTTP/1.1 responses keep the connection open and serve subsequent
160requests on the same socket. The server honors an explicit `Connection: close`
161and closes HTTP/1.0 connections by default — no configuration required.
162Streaming responses of unknown length (SSE, procedure bodies) are framed with
163`Transfer-Encoding: chunked`.
165### Form Parsing
167```scheme
168;; URL-encoded form data
169(let ((form (parse-form-urlencoded (http-request-body req))))
170 (dict-ref form username:))
172;; Multipart form data (file uploads)
173(let ((parts (parse-form-data req)))
174 (dict-ref parts file:))
175```
177### Server-Sent Events (SSE)
179```scheme
180;; Single client SSE stream
181(http-response/sse
182 (lambda (send)
183 (send (sse-event "message" (json-encode #{ count: 1 })))
184 (send (sse-data "plain text data"))))
186;; Broadcast to multiple clients
187(http-response/sse-broadcast channel)
188```
190## Status Code Constants
192```scheme
193HTTP-OK ; 200
194HTTP-CREATED ; 201
195HTTP-NO-CONTENT ; 204
196HTTP-PARTIAL-CONTENT ; 206
197HTTP-MOVED-PERMANENTLY ; 301
198HTTP-FOUND ; 302
199HTTP-NOT-MODIFIED ; 304
200HTTP-BAD-REQUEST ; 400
201HTTP-RANGE-NOT-SATISFIABLE ; 416
202HTTP-UNAUTHORIZED ; 401
203HTTP-FORBIDDEN ; 403
204HTTP-NOT-FOUND ; 404
205HTTP-INTERNAL-SERVER-ERROR ; 500
206```
208## Common Patterns
210### REST API Server
212```scheme
213(import (sigil http)
214 (sigil json))
216(define users #{ 1: #{ name: "Alice" } 2: #{ name: "Bob" } })
218(http-serve 8080
219 (lambda (req)
220 (let ((method (http-request-method req))
221 (path (http-request-path req)))
222 (cond
223 ;; GET /users
224 ((and (eq? method 'GET) (string=? path "/users"))
225 (http-response/json 200 users))
227 ;; GET /users/:id
228 ((and (eq? method 'GET) (string-starts-with? path "/users/"))
229 (let* ((id (string->number (substring path 7 (string-length path))))
230 (user (dict-ref users id #f)))
231 (if user
232 (http-response/json 200 user)
233 (http-response/not-found))))
235 ;; POST /users
236 ((and (eq? method 'POST) (string=? path "/users"))
237 (let ((data (json-decode (http-request-body req))))
238 (http-response/json 201 #{ id: 3 name: (dict-ref data name:) })))
240 (else (http-response/not-found))))))
241```
243### Fetch and Process JSON API
245```scheme
246(import (sigil http)
247 (sigil json))
249(define (fetch-user id)
250 (let ((response (http-get (format "https://api.example.com/users/~a" id))))
251 (if (= (http-response-status response) 200)
252 (json-decode (http-response-body response))
253 #f)))
255(let ((user (fetch-user 123)))
256 (when user
257 (display (dict-ref user name:))))
258```