Summary
The http package implements code for a simple HTTP client and server. There
pkg http =
type status = int
type session
type server
/*
* The full list of status codes is enumerated at the
* bottom of this page.
*/
type url = struct
schema : schema
port : uint16
host : byte[:]
path : byte[:]
params : (byte[:], byte[:])[:]
;;
type req = struct
url : url#
hdrs : (byte[:], byte[:])[:]
err : std.option(err)
method : method
;;
type resp = struct
status : status /* http status */
hdrs : (byte[:], byte[:])[:] /* list of headers */
len : std.size /* length of response */
err : std.option(err) /* error, if present */
reason : byte[:] /* reason text for error */
body : byte[:] /* body of response or error */
;;
type err = union
`Ehttp int /* http error */
`Eunsupp /* unsupported feature */
`Econn /* connection lost */
`Ehdr /* invalid header */
`Eproto /* protocol error */
`Eshort /* truncated response */
`Egarbled /* syntax error */
`Eenc /* encoding error */
`Ewat /* unknown error */
;;
type schema = union
`Http
`Https
;;
type method = union
`Get
`Head
`Put
`Post
`Delete
`Trace
`Options
;;
type encoding = union
`Length
`Chunked
`Compress
`Deflate
`Gzip
;;
;;
These types are shared between both the client and server APIs. Currently, there are two major features missing: HTTPS, and
type status = int
This is the HTTP status response. All the valid types are listed later down this page.
type session
This represents a single session with a server or client. Multiple requests can reuse the same connection, and all requests on a connection are sent in order.
type server
This is the HTTP server. It implements a simple, multithreaded HTTP server. It currently uses a simple thread per connection model.
Client API
pkg http =
/* convenience api */
const get : (s : session#, r : byte[:] -> std.result(byte[:], err))
const head : (s : session#, r : byte[:] -> std.result(byte[:], err))
const put : (s : session#, r : byte[:], data : byte[:] -> std.result(byte[:], err))
const post : (s : session#, r : byte[:], data : byte[:] -> std.result(byte[:], err))
const delete : (s : session#, r : byte[:] -> std.result(byte[:], err))
const options : (s : session#, r : byte[:] -> std.result(byte[:], err))
const trace : (s : session#, r : byte[:] -> std.result(byte[:], err))
/* request based versions */
const getreq : (s : session#, r : req# -> std.result(resp#, err))
const headreq : (s : session#, r : req# -> std.result(resp#, err))
const putreq : (s : session#, r : req#, data : byte[:] -> std.result(resp#, err))
const postreq : (s : session#, r : req#, data : byte[:] -> std.result(resp#, err))
const deletereq : (s : session#, r : req# -> std.result(resp#, err))
const optionsreq : (s : session#, r : req# -> std.result(resp#, err))
const tracereq : (s : session#, r : req# -> std.result(resp#, err))
const freeresp : (r : resp# -> void)
;;
Server API
pkg http =
const announce : (ds : byte[:] -> std.result(server#, err))
const shutdown : (srv : server# -> void)
const serve : (srv : server#, fn : (srv : server#, s : session#, req : req# -> void) -> void)
const respond : (srv : server#, sess : session#, resp : resp# -> void)
;;
The server is an plain, mutlithreaded server. Multiple requests can be processed in parallel.
const announce : (ds : byte[:] -> std.result(server#, err))
Announce creates the server on the dialstring ds
. This server does not
begin to accept connections until the serve
call is hit.
const shutdown : (srv : server# -> void)
Shutdown waits for all in-flight requests to be closed, then releases all of the associated resources.
const serve : (srv : server#, fn : (srv : server#, s : session#, req : req# -> void) -> void)
Serve starts handling requests. For every request, it calls the callback
function fn
. The callback is responsible for routing the requests to the
appropriate resources, and generating the response.
Status Codes
pkg http =
const Continue : status = 100 /* RFC 7231, 6.2.1*/
const SwitchingProtocols : status = 101 /* RFC 7231, 6.2.2*/
const Processing : status = 102 /* RFC 2518, 10.1*/
const Ok : status = 200 /* RFC 7231, 6.3.1*/
const Created : status = 201 /* RFC 7231, 6.3.2*/
const Accepted : status = 202 /* RFC 7231, 6.3.3*/
const NonAuthoritativeInfo : status = 203 /* RFC 7231, 6.3.4*/
const NoContent : status = 204 /* RFC 7231, 6.3.5*/
const ResetContent : status = 205 /* RFC 7231, 6.3.6*/
const PartialContent : status = 206 /* RFC 7233, 4.1*/
const Multi : status = 207 /* RFC 4918, 11.1*/
const AlreadyReported : status = 208 /* RFC 5842, 7.1*/
const IMUsed : status = 226 /* RFC 3229, 10.4.1*/
const MultipleChoices : status = 300 /* RFC 7231, 6.4.1*/
const MovedPermanently : status = 301 /* RFC 7231, 6.4.2*/
const Found : status = 302 /* RFC 7231, 6.4.3*/
const SeeOther : status = 303 /* RFC 7231, 6.4.4*/
const NotModified : status = 304 /* RFC 7232, 4.1*/
const UseProxy : status = 305 /* RFC 7231, 6.4.5*/
const TemporaryRedirect : status = 307 /* RFC 7231, 6.4.7*/
const PermanentRedirect : status = 308 /* RFC 7538, 3*/
const BadRequest : status = 400 /* RFC 7231, 6.5.1*/
const Unauthorized : status = 401 /* RFC 7235, 3.1*/
const PaymentRequired : status = 402 /* RFC 7231, 6.5.2*/
const Forbidden : status = 403 /* RFC 7231, 6.5.3*/
const NotFound : status = 404 /* RFC 7231, 6.5.4*/
const MethodNotAllowed : status = 405 /* RFC 7231, 6.5.5*/
const NotAcceptable : status = 406 /* RFC 7231, 6.5.6*/
const ProxyAuthRequired : status = 407 /* RFC 7235, 3.2*/
const RequestTimeout : status = 408 /* RFC 7231, 6.5.7*/
const Conflict : status = 409 /* RFC 7231, 6.5.8*/
const Gone : status = 410 /* RFC 7231, 6.5.9*/
const LengthRequired : status = 411 /* RFC 7231, 6.5.10*/
const PreconditionFailed : status = 412 /* RFC 7232, 4.2*/
const RequestEntityTooLarge : status = 413 /* RFC 7231, 6.5.11*/
const RequestURITooLong : status = 414 /* RFC 7231, 6.5.12*/
const UnsupportedMediaType : status = 415 /* RFC 7231, 6.5.13*/
const RangeNotSatisfiable : status = 416 /* RFC 7233, 4.4*/
const ExpectationFailed : status = 417 /* RFC 7231, 6.5.14*/
const Teapot : status = 418 /* RFC 7168, 2.3.3*/
const UnprocessableEntity : status = 422 /* RFC 4918, 11.2*/
const Locked : status = 423 /* RFC 4918, 11.3*/
const FailedDependency : status = 424 /* RFC 4918, 11.4*/
const UpgradeRequired : status = 426 /* RFC 7231, 6.5.15*/
const PreconditionRequired : status = 428 /* RFC 6585, 3*/
const TooManyRequests : status = 429 /* RFC 6585, 4*/
const HeaderFieldsTooLarge : status = 431 /* RFC 6585, 5*/
const UnavailableForLegal : status = 451 /* RFC 7725, 3*/
const InternalServerError : status = 500 /* RFC 7231, 6.6.1*/
const NotImplemented : status = 501 /* RFC 7231, 6.6.2*/
const BadGateway : status = 502 /* RFC 7231, 6.6.3*/
const ServiceUnavailable : status = 503 /* RFC 7231, 6.6.4*/
const GatewayTimeout : status = 504 /* RFC 7231, 6.6.5*/
const VersionNotSupported : status = 505 /* RFC 7231, 6.6.6*/
const VariantAlsoNegotiates : status = 506 /* RFC 2295, 8.1*/
const InsufficientStorage : status = 507 /* RFC 4918, 11.5*/
const LoopDetected : status = 508 /* RFC 5842, 7.2*/
const NotExtended : status = 510 /* RFC 2774, 7*/
const NetworkAuthRequired : status = 511 /* RFC 6585, 6*/
;;
These are standard HTTP codes. For detailed meanings, see the relevant RFCs.
Example Client
const main = {args
var u, s, r
cmd = std.optparse(args, &[
.argdesc = "url...",
])
for url : cmd.args
u = std.try(http.parseurl(url))
s = std.try(http.mksession(u.schema, u.host, u.port))
r = std.try(http.get(s, u))
std.put("{}\n", r.body)
http.freeresp(r)
}
Example Server
A simple server that serves the contents of a directory to localhost. This program doens't attempt to sanitize paths, so it's not recommended to put it directly on the internet.
use std
use http
const main = {
var srv, cmd
match http.announce("tcp!localhost!8080")
| `std.Ok s: srv = s
| `std.Err e: std.fatal("unable to announce: {}\n", e)
;;
http.serve(srv, handler)
}
const handler = {srv, sess, req
std.put("Reading path {}\n", req.url.path)
match req.url.path
| "/ping": respond(srv, sess, 200, "pong")
| fspath: showfile(srv, sess, req.url.path)
;;
}
const showfile = {srv, sess, path
var eb : byte[128]
var p
p = std.pathcat(".", path)
match std.slurp(p)
| `std.Ok buf:
respond(srv, sess, 200, buf)
std.slfree(buf)
| `std.Err e:
respond(srv, sess, 404, std.bfmt(eb[:], "error reading {}: {}\n", p, e))
;;
std.slfree(p)
}
const respond = {srv, sess, status, body
var resp
resp = std.mk([
.status=status,
.hdrs = [][:],
.len = 0,
.err = `std.None,
.reason = "",
.body = body,
.enc = `http.Length
])
http.respond(srv, sess, resp)
std.free(resp)
}
Status Codes
const Continue : status = 100 /* RFC 7231, 6.2.1*/
const SwitchingProtocols : status = 101 /* RFC 7231, 6.2.2*/
const Processing : status = 102 /* RFC 2518, 10.1*/
const Ok : status = 200 /* RFC 7231, 6.3.1*/
const Created : status = 201 /* RFC 7231, 6.3.2*/
const Accepted : status = 202 /* RFC 7231, 6.3.3*/
const NonAuthoritativeInfo : status = 203 /* RFC 7231, 6.3.4*/
const NoContent : status = 204 /* RFC 7231, 6.3.5*/
const ResetContent : status = 205 /* RFC 7231, 6.3.6*/
const PartialContent : status = 206 /* RFC 7233, 4.1*/
const Multi : status = 207 /* RFC 4918, 11.1*/
const AlreadyReported : status = 208 /* RFC 5842, 7.1*/
const IMUsed : status = 226 /* RFC 3229, 10.4.1*/
const MultipleChoices : status = 300 /* RFC 7231, 6.4.1*/
const MovedPermanently : status = 301 /* RFC 7231, 6.4.2*/
const Found : status = 302 /* RFC 7231, 6.4.3*/
const SeeOther : status = 303 /* RFC 7231, 6.4.4*/
const NotModified : status = 304 /* RFC 7232, 4.1*/
const UseProxy : status = 305 /* RFC 7231, 6.4.5*/
const TemporaryRedirect : status = 307 /* RFC 7231, 6.4.7*/
const PermanentRedirect : status = 308 /* RFC 7538, 3*/
const BadRequest : status = 400 /* RFC 7231, 6.5.1*/
const Unauthorized : status = 401 /* RFC 7235, 3.1*/
const PaymentRequired : status = 402 /* RFC 7231, 6.5.2*/
const Forbidden : status = 403 /* RFC 7231, 6.5.3*/
const NotFound : status = 404 /* RFC 7231, 6.5.4*/
const MethodNotAllowed : status = 405 /* RFC 7231, 6.5.5*/
const NotAcceptable : status = 406 /* RFC 7231, 6.5.6*/
const ProxyAuthRequired : status = 407 /* RFC 7235, 3.2*/
const RequestTimeout : status = 408 /* RFC 7231, 6.5.7*/
const Conflict : status = 409 /* RFC 7231, 6.5.8*/
const Gone : status = 410 /* RFC 7231, 6.5.9*/
const LengthRequired : status = 411 /* RFC 7231, 6.5.10*/
const PreconditionFailed : status = 412 /* RFC 7232, 4.2*/
const RequestEntityTooLarge : status = 413 /* RFC 7231, 6.5.11*/
const RequestURITooLong : status = 414 /* RFC 7231, 6.5.12*/
const UnsupportedMediaType : status = 415 /* RFC 7231, 6.5.13*/
const RangeNotSatisfiable : status = 416 /* RFC 7233, 4.4*/
const ExpectationFailed : status = 417 /* RFC 7231, 6.5.14*/
const Teapot : status = 418 /* RFC 7168, 2.3.3*/
const UnprocessableEntity : status = 422 /* RFC 4918, 11.2*/
const Locked : status = 423 /* RFC 4918, 11.3*/
const FailedDependency : status = 424 /* RFC 4918, 11.4*/
const UpgradeRequired : status = 426 /* RFC 7231, 6.5.15*/
const PreconditionRequired : status = 428 /* RFC 6585, 3*/
const TooManyRequests : status = 429 /* RFC 6585, 4*/
const HeaderFieldsTooLarge : status = 431 /* RFC 6585, 5*/
const UnavailableForLegal : status = 451 /* RFC 7725, 3*/
const InternalServerError : status = 500 /* RFC 7231, 6.6.1*/
const NotImplemented : status = 501 /* RFC 7231, 6.6.2*/
const BadGateway : status = 502 /* RFC 7231, 6.6.3*/
const ServiceUnavailable : status = 503 /* RFC 7231, 6.6.4*/
const GatewayTimeout : status = 504 /* RFC 7231, 6.6.5*/
const VersionNotSupported : status = 505 /* RFC 7231, 6.6.6*/
const VariantAlsoNegotiates : status = 506 /* RFC 2295, 8.1*/
const InsufficientStorage : status = 507 /* RFC 4918, 11.5*/
const LoopDetected : status = 508 /* RFC 5842, 7.2*/
const NotExtended : status = 510 /* RFC 2774, 7*/
const NetworkAuthRequired : status = 511 /* RFC 6585, 6*/