AtlatestRepositorysigil-http
sigil-http / tree / docshttp.md
1
# HTTP3
> HTTP/1.1 client and server functionality.5
```scheme6
(import (sigil http))7
```9
## Client11
### http-get13
Make an HTTP GET request.15
```scheme16
(let ((response (http-get "https://example.com/")))17
(display (http-response-body response)))19
;; With custom headers20
(http-get "https://api.example.com/data"21
'(("Authorization" . "Bearer token123")))22
```24
### http-post26
Make an HTTP POST request.28
```scheme29
;; Form data (default Content-Type: application/x-www-form-urlencoded)30
(http-post "https://example.com/login"31
"username=alice&password=secret")33
;; JSON body34
(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-delete42
```scheme43
(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-request52
Low-level request function for any HTTP method.54
```scheme55
(http-request 'PATCH "https://api.example.com/resource"56
'(("Content-Type" . "application/json"))57
(json-encode #{ status: "active" }))58
```60
### Response Accessors62
```scheme63
(let ((response (http-get "https://example.com/")))64
(http-response-status response) ; => 20065
(http-response-headers response) ; => #{ content-type: "text/html" ... }66
(http-response-body response)) ; => "<html>..."67
```69
### URL Parsing71
```scheme72
(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) ; => 808076
(url-path url) ; => "/path"77
(url-query url)) ; => "query=1"78
```80
## Server82
### http-serve84
Start a blocking HTTP server.86
```scheme87
(http-serve 808088
(lambda (request)89
(http-response/html 200 "<h1>Hello!</h1>")))90
```92
### Request Accessors94
```scheme95
(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 #f99
(http-request-headers req) ; => #{ content-type: "..." ... }100
(http-request-body req) ; => string or #f101
(http-request-header req content-type:)) ; => "application/json"102
```104
### Response Constructors106
```scheme107
;; Plain text108
(http-response/text 200 "Hello, World!")110
;; HTML111
(http-response/html 200 "<h1>Welcome</h1>")113
;; JSON (auto-encodes)114
(http-response/json 200 #{ status: "ok" users: #[1 2 3] })116
;; Redirect117
(http-response/redirect "/new-location")118
(http-response/redirect "/new-location" 301) ; Permanent120
;; Error121
(http-response/error 500 "Internal Server Error")123
;; Not Found124
(http-response/not-found)125
```127
### Custom Response129
```scheme130
(http-response131
status: 201132
headers: #{ content-type: "application/json"133
x-custom-header: "value" }134
body: (json-encode #{ id: 123 }))135
```137
### File Serving and Range Requests139
`http-response/file` streams a file from disk with an auto-detected MIME type.140
Pass `range:` the request's raw `Range` header to honor byte ranges — media141
seeking and resumable downloads:143
```scheme144
;; Whole file (200 OK), advertises Accept-Ranges: bytes145
(http-response/file "/srv/video.mp4")147
;; Honor the request Range header: 206 Partial Content for a satisfiable148
;; 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
```153
Supported range forms: `bytes=A-B` (first–last), `bytes=A-` (open-ended), and154
`bytes=-N` (the last N bytes). A `206` response carries `Content-Range` and the155
exact `Content-Length`; a `416` carries `Content-Range: bytes */<total>`.157
### Persistent Connections (keep-alive)159
Non-streaming HTTP/1.1 responses keep the connection open and serve subsequent160
requests on the same socket. The server honors an explicit `Connection: close`161
and closes HTTP/1.0 connections by default — no configuration required.162
Streaming responses of unknown length (SSE, procedure bodies) are framed with163
`Transfer-Encoding: chunked`.165
### Form Parsing167
```scheme168
;; URL-encoded form data169
(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
```scheme180
;; Single client SSE stream181
(http-response/sse182
(lambda (send)183
(send (sse-event "message" (json-encode #{ count: 1 })))184
(send (sse-data "plain text data"))))186
;; Broadcast to multiple clients187
(http-response/sse-broadcast channel)188
```190
## Status Code Constants192
```scheme193
HTTP-OK ; 200194
HTTP-CREATED ; 201195
HTTP-NO-CONTENT ; 204196
HTTP-PARTIAL-CONTENT ; 206197
HTTP-MOVED-PERMANENTLY ; 301198
HTTP-FOUND ; 302199
HTTP-NOT-MODIFIED ; 304200
HTTP-BAD-REQUEST ; 400201
HTTP-RANGE-NOT-SATISFIABLE ; 416202
HTTP-UNAUTHORIZED ; 401203
HTTP-FORBIDDEN ; 403204
HTTP-NOT-FOUND ; 404205
HTTP-INTERNAL-SERVER-ERROR ; 500206
```208
## Common Patterns210
### REST API Server212
```scheme213
(import (sigil http)214
(sigil json))216
(define users #{ 1: #{ name: "Alice" } 2: #{ name: "Bob" } })218
(http-serve 8080219
(lambda (req)220
(let ((method (http-request-method req))221
(path (http-request-path req)))222
(cond223
;; GET /users224
((and (eq? method 'GET) (string=? path "/users"))225
(http-response/json 200 users))227
;; GET /users/:id228
((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 user232
(http-response/json 200 user)233
(http-response/not-found))))235
;; POST /users236
((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 API245
```scheme246
(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 user257
(display (dict-ref user name:))))258
```