sigildocs

(sigil http client)

(sigil http client) - HTTP Client Implementation

Provides HTTP/1.1 client functionality for making requests to HTTP and HTTPS servers.

Example: (import (sigil http client)) (let ((response (http-get "https://example.com/"))) (display (http-response-body response)))

Exports

urlprocedure

Construct a url struct.

url?procedure

Test if a value is a url struct.

url-schemeprocedure

Get the scheme field of a url struct.

url-hostprocedure

Get the host field of a url struct.

url-portprocedure

Get the port field of a url struct.

url-pathprocedure

Get the path field of a url struct.

url-queryprocedure

Get the query field of a url struct.

parse-urlprocedure

Parse a URL string into a url record.

Supports http://host:port/path?query and https://.... Use url-scheme, url-host, url-port, url-path, url-query to access the components.

(let ((u (parse-url "https://example.com:8080/api?key=val")))
  (url-host u))  ; => "example.com"

Connect to a server, using TLS if scheme is https Returns connection object or #f on failure

conn-writeprocedure

Write data to connection (socket or TLS)

conn-readprocedure

Read data from connection (socket or TLS)

Read binary data from connection as bytevector (socket or TLS)

conn-closeprocedure

Close connection

Build HTTP request string

Build header lines from headers (dict or alist)

Parse HTTP status line Returns (version status-code reason) or #f

Parse response headers from data Returns dict with keyword keys

Read HTTP response from connection Returns http-response or #f on error

read-all-dataprocedure

Read all data from connection until closed. Reads as bytevectors and converts to string once at the end to avoid splitting multi-byte UTF-8 characters across chunks.

Parse HTTP response from string

Decode chunked transfer encoding using byte-level operations. Chunk sizes in HTTP are byte counts, so we must work with bytes to correctly handle multi-byte UTF-8 content. Format: <hex-size>rn<data>rn ... 0rnrn

Find position of rn in a bytevector starting at pos

find-crlfprocedure

Find position of rn in a string starting at pos

Convert hex string to number

Find the end of HTTP headers (position of rnrn)

skip-crlfprocedure

Skip CRLF sequence(s) at position

http-requestvariable

Make an HTTP request.

Low-level function for making HTTP requests. Prefer the convenience functions (http-get, http-post, etc.) for common cases.

(http-request 'GET "https://api.example.com/users"
              headers: #{ authorization: "Bearer token" })

(http-request 'POST "https://api.example.com/users"
              headers: #{ content-type: "application/json" }
              body: "{\"name\": \"Alice\"}")
http-getvariable

HTTP GET request.

(http-get "https://example.com/")

(http-get "https://api.example.com/users"
          headers: #{ authorization: "Bearer token" })
http-postvariable

HTTP POST request.

If no Content-Type header is provided, defaults to application/x-www-form-urlencoded.

(http-post "https://api.example.com/data" "key=value")

(http-post "https://api.example.com/data"
           "{\"key\": \"value\"}"
           headers: #{ content-type: "application/json" })
http-putvariable

HTTP PUT request.

(http-put "https://api.example.com/users/123"
          "{\"name\": \"Alice\"}"
          headers: #{ content-type: "application/json" })
http-deletevariable

HTTP DELETE request.

(http-delete "https://api.example.com/users/123")

(http-delete "https://api.example.com/users/123"
             headers: #{ authorization: "Bearer token" })
http-headvariable

HTTP HEAD request.

Like GET but only retrieves headers, not body. Useful for checking if a resource exists or getting metadata.

(let ((res (http-head "https://example.com/file.pdf")))
  (http-response-header res "Content-Length"))
http-optionsvariable

HTTP OPTIONS request.

Query server for allowed methods on a resource.

(let ((res (http-options "https://api.example.com/users")))
  (http-response-header res "Allow"))
; => "GET, POST, OPTIONS"
http-patchvariable

HTTP PATCH request.

Partially update a resource. Unlike PUT which replaces the entire resource, PATCH applies partial modifications.

(http-patch "https://api.example.com/users/123"
            "{\"email\": \"new@example.com\"}"
            headers: #{ content-type: "application/json" })

Parse HTTP response body as JSON.

Returns the parsed JSON value, or #f if the response is #f or parsing fails. Requires sigil-json package.

(let ((res (http-get "https://api.example.com/data")))
  (http-response-json res))
; => #{ users: #[...] count: 42 }

HTTP GET request expecting JSON response.

Makes a GET request and parses the response body as JSON. Returns the parsed JSON value, or #f if request fails or status is not 2xx.

(http-get/json "https://api.example.com/users")
; => #{ users: #[...] }

(http-get/json "https://api.example.com/users"
               headers: #{ authorization: "Bearer token" })

HTTP POST request with JSON body, expecting JSON response.

Encodes the body as JSON, sets Content-Type to application/json, and parses the response as JSON. Returns parsed JSON on any status code, or #f if the request failed entirely.

(http-post/json "https://api.example.com/users"
                #{ name: "Alice" email: "alice@example.com" })
; => #{ id: 123 name: "Alice" }

(http-post/json "https://api.example.com/users"
                #{ name: "Alice" }
                headers: #{ authorization: "Bearer token" })

Read HTTP response headers only, without consuming the body.

Returns a list: (status-code headers leftover-string) where leftover-string is any body data already read past the header boundary. Returns #f on failure.

Parse headers from data at the given header-end position. Returns (status-code headers leftover-string).

Stream response body from connection to an output port.

Writes leftover bytes (from header read) first, then reads remaining data as bytevectors and writes them to the port.

http-downloadprocedure

Download a URL to a file, streaming data directly to disk.

Unlike http-get which loads the entire response into memory, http-download streams the response body to a file, making it suitable for large downloads.

The on-progress callback receives (bytes-received total-bytes) where total-bytes may be #f if the server didn't send Content-Length.

Returns a dict with download info on success, or #f on failure.

(http-download "https://example.com/large-file.bin"
               "/tmp/file.bin")
; => #{ status: 200 size: 12345 path: "/tmp/file.bin" }

(http-download "https://example.com/file.bin"
               "/tmp/file.bin"
               on-progress: (lambda (received total)
                              (display (str received "/" total "\r"))))
build-api-urlprocedure

Build an API URL from a base URL and path segments.

Common pattern across API clients: concatenate a base URL with slash-separated path parts.

(build-api-url "https://api.example.com" "v1" "users" "123")
; => "https://api.example.com/v1/users/123"

Create a response checker function for an API client.

Takes a name for error messages and an optional list of (status-code . message) pairs for specific error handling. Returns a function that checks an HTTP response and either returns parsed JSON on success or raises an error.

The optional parse-error keyword accepts a function (lambda (status body) ...) for custom error body parsing (e.g., JSON:API error extraction). When provided, it is called instead of the default handler for status >= 400 that don't match a specific handler entry.

(define check-response
  (make-response-checker
    name: "YouTube API"
    handlers: (list
      (cons 401 "Access token may be expired.")
      (cons 403 "Possible quota exceeded."))))

(check-response (http-get url headers: auth))
make-json-apiprocedure

Create authenticated JSON API method wrappers.

Takes a function that returns auth headers and a response checker, and returns a dict with get, post, put, patch, and delete functions that handle JSON encoding/decoding and authentication.

(define api (make-json-api
              auth-headers: (lambda () #{ authorization: "Bearer tok" })
              check-response: my-checker))

((dict-ref api get:) "https://api.example.com/users")
((dict-ref api post:) "https://api.example.com/users" #{ name: "Alice" })

(No description)

(No description)

(No description)

(No description)