LUA-HTTP(3)
โจ Introduction โจ
okay so bestie, welcome to lua-http(3) โ the manual for lua-http, a performant, capable HTTP and WebSocket library for Lua 5.1, 5.2, 5.3 and LuaJIT. No cap, it's giving networking main character energy. Some of the features of the library include:
- Support for HTTP versions 1, 1.1 and 2 as specified by RFC 7230 and RFC 7540. Slay.
- Provides both client and server APIs. Both sides of the convo, we love to see it.
- Fully asynchronous API that does not block the current thread when executing operations that typically block. Not me blocking the main thread? Never.
- Support for WebSockets as specified by RFC 6455 including ping/pong, binary data transfer and TLS encryption. Ping/pong is literally self-care.
- Transport Layer Security (TLS) โ lua-http supports HTTPS and WSS via luaossl. TLS? Obsessed.
- Easy integration into other event-loop or scripts. It just slides in, no drama.
Why lua-http? ๐
The lua-http library was written to fill a gap in the Lua ecosystem by providing an HTTP and WebSocket library with the following traits:
- Asynchronous and performant. Say it with me: async and fast.
- Can be used without forcing the developer to follow a specific pattern. Conversely, the library can be adapted to many common patterns. Your pattern, your rules.
- Can be used at a very high level without need to understand the transportation of HTTP data (other than connection addresses). High-level bestie era.
- Provides a rich low level API, if desired, for creating powerful HTTP based tools at the protocol level. And if you wanna go low-level? It ate.
As a result of these design goals, the library is simple and unobtrusive and can accommodate tens of thousands of connections on commodity hardware. Tens of thousands. On commodity hardware. Iconic.
lua-http is a flexible HTTP and WebSocket library that allows developers to concentrate on line-of-business features when building Internet enabled applications. If you are looking for a way to streamline development of an internet enabled application, enable HTTP networking in your game, create a new Internet Of Things (IoT) system, or write a performant custom web server for a specific use case, lua-http has the tools you need. Periodt.
Portability ๐
lua-http is pure Lua code with dependencies on the following external libraries:
- cqueues โ Posix API library for Lua. The async backbone.
- luaossl โ Lua bindings for TLS/SSL. Security queen.
- lua-zlib โ Optional Lua bindings for zlib. Optional, but cute.
lua-http can run on any operating system supported by cqueues and openssl, which at the time of writing is GNU/Linux, FreeBSD, NetBSD, OpenBSD, OSX and Solaris. Portability? We love her.
Common Use Cases ๐
The following are two simple demonstrations of how the lua-http library can be used:
Retrieving a Document ๐
The highest level interface for clients is http.request. By constructing a request object from a URI using new_from_uri and immediately evaluating it, you can easily fetch an HTTP resource. Like, fetch? Slay.
local http_request = require "http.request"
local headers, stream = assert(http_request.new_from_uri("http://example.com"):go())
local body = assert(stream:get_body_as_string())
if headers:get ":status" ~= "200" then
error(body)
end
print(body)
WebSocket Communications ๐ฆ
To request information from a WebSocket server, use the websocket module to create a new WebSocket client. Hello, real-time bestie.
local websocket = require "http.websocket"
local ws = websocket.new_from_uri("wss://echo.websocket.org")
assert(ws:connect())
assert(ws:send("koo-eee!"))
local data = assert(ws:receive())
assert(data == "koo-eee!")
assert(ws:close())
Asynchronous Operation โก
lua-http has been written to perform asynchronously so that it can be used in your application, server or game without blocking your main loop. Asynchronous operations are achieved by utilizing cqueues, a Lua/C library that incorporates Lua yielding and kernel level APIs to reduce CPU usage. All lua-http operations including DNS lookup, TLS negotiation and read/write operations will not block the main application thread when run from inside a cqueue or cqueue enabled โcontainerโ. While sometimes it is necessary to block a routine (yield) and wait for external data, any blocking API calls take an optional timeout to ensure good behaviour of networked applications and avoid unresponsive or โdeadโ routines. No dead routines in this house.
Asynchronous operations are one of the most powerful features of lua-http and require no effort on the developers part. For instance, an HTTP server can be instantiated within any Lua main loop and run alongside application code without adversely affecting the main application process. If other cqueue enabled components are integrated within a cqueue loop, the application is entirely event driven through kernel level polling APIs. Event-driven? It's giving kernel-level polling royalty.
cqueues can be used in conjunction with lua-http to integrate other features into your lua application and create powerful, performant, web enabled applications. Some of the examples in this guide will use cqueues for simple demonstrations. For more resources about cqueues, please see:
- The cqueues website for more information about the cqueues library. Go read, bestie.
- cqueues examples can be found with the cqueues source code available through git or archives or accessed online here. Source code era.
- For more information on integrating cqueues with other event loop libraries please see integration with other event loops. Loop integration, iykyk.
- For other libraries that use cqueues such as asynchronous APIs for Redis and PostgreSQL, please see the cqueues wiki entry here. Redis and PostgreSQL? The crossover event.
Conventions ๐
The following is a list of API conventions and general reference:
HTTP ๐
- HTTP 1 request and status line fields are passed around inside of
headersobjects under keys":authority",":method",":path",":scheme"and":status"as defined in HTTP 2. As such, they are all kept in string form (important to remember for the:statusfield). Internalize it. - Header fields should always be used with lower case keys. Lowercase or it's not invited.
Errors ๐
- Invalid function parameters will throw a lua error (if validated). Rude, but fair.
- Errors are returned as
nil, error, errnounless noted otherwise. Remember the trio. - Some HTTP 2 operations return/throw special http 2 error objects. Special little error objects.
Timeouts โณ
All operations that may block the current thread take a timeout argument. This argument is always the number of seconds to allow before returning nil, err_msg, ETIMEDOUT where err_msg is a localised error message such as "connection timed out". Timeout? We don't ghost, we set boundaries.
Terminology ๐ง
Much lua-http terminology is borrowed from HTTP 2.
Connection โ An abstraction over an underlying TCP/IP socket. lua-http currently has two connection types: one for HTTP 1, one for HTTP 2. Two connection types, one vibe.
Stream โ A request/response on a connection object. lua-http has two stream types: one for HTTP 1 streams, and one for HTTP 2 streams. The common interfaces is described in stream. Streams are the moment.
๐ Interfaces ๐
ok so bestie, here's the tea โ โ lua-http keeps HTTP 1 and HTTP 2 in separate modules, but they're giving the same energy underneath, so the library hands you one shared interface for everything that makes sense in both worlds (and whatever future protocol glow-up comes next ๐
).
The next few sections are the whole interface menu, no gatekeeping.
๐ connection ๐
A connection is basically a socket wrapped in a lil protocol-specific jacket. It may have streams on it โ those are the individual request/response situations happening over the connection. OR you can fully ignore streams and just do low-level protocol stuff straight on the socket. Your call, main character โจ.
Every connection type exposes these fields:
connection.type
The mode of use for the connection object. Valid values are:
"client": Acts as a client; this connection type is used by entities who want to make requests"server": Acts as a server; this connection type is used by entities who want to respond to requests
connection.version
The HTTP version number of the connection, as a number. ๐ข
connection:pollfd()
connection:events()
connection:timeout()
connection:connect(timeout)
Completes the connection to the remote server using the address specified, the HTTP version and any options you gave in the connection.new constructor. connect will yield until the connection attempt finishes (success or failure) or until timeout is exceeded. Connecting may include DNS lookups, TLS negotiation and HTTP2 settings exchange โ it's a whole journey ๐ซ.
Returns true on success. On error, returns nil, an error message and an error number.
connection:checktls()
Checks the socket for a valid Transport Layer Security connection. Returns the luaossl ssl object if the connection is secured ๐. Returns nil and an error message if there's no active TLS session. Go peep the luaossl website for more info about the ssl object, iykyk.
connection:localname()
Returns the connection information for the local socket. Returns address family, IP address and port for an external socket. For Unix domain sockets, the function returns AF_UNIX and the path. If the connection object isn't connected, returns AF_UNSPEC (0). On error, returns nil, an error message and an error number.
connection:peername()
Returns the connection information for the socket peer (as in, the next hop ๐). Returns address family, IP address and port for an external socket. For unix sockets, the function returns AF_UNIX and the path. If the connection object isn't connected, returns AF_UNSPEC (0). On error, returns nil, an error message and an error number.
Note: If the client is using a proxy, the values returned by :peername() point to the proxy, not the remote server. Don't get it twisted ๐ซถ
connection:flush(timeout)
Flushes buffered outgoing data on the socket to the operating system. Returns true on success. On error, returns nil, an error message and an error number.
connection:shutdown()
Performs an orderly shutdown of the connection by closing all streams and calls :shutdown() on the socket. The connection cannot be re-opened. It's over, it's giving closed-for-business ๐ญ
connection:close()
Closes a connection and releases operating system resources. Note that :close() performs a connection:shutdown() prior to releasing resources.
connection:new_stream()
Creates and returns a new stream on the connection. Fresh stream, who dis โจ
connection:get_next_incoming_stream(timeout)
Returns the next peer initiated stream on the connection. This function can be used to yield and "listen" for incoming HTTP streams ๐.
connection:onidle(new_handler)
Provide a callback to get called when the connection becomes idle, i.e. when there's no request in progress and no pipelined streams waiting. When called it will receive the connection as the first argument. Returns the previous handler.
๐ธ stream ๐ธ
An HTTP stream is an abstraction of a request/response within an HTTP connection. Within a stream there may be a number of "header" blocks, plus data known as the "body" ๐.
All stream types expose the following fields and functions:
stream.connection
The underlying connection object.
stream:checktls()
Convenience wrapper equivalent to stream.connection:checktls()
stream:localname()
Convenience wrapper equivalent to stream.connection:localname()
stream:peername()
Convenience wrapper equivalent to stream.connection:peername()
stream:get_headers(timeout)
Retrieves the next complete headers object (i.e. a block of headers or trailers) from the stream.
stream:write_headers(headers, end_stream, timeout)
Write the given headers object to the stream. The function takes a flag indicating if this is the last chunk in the stream โ if true the stream will be closed. If timeout is specified, the stream will wait for the send to complete until timeout is exceeded.
stream:write_continue(timeout)
Sends a 100-continue header block.
stream:get_next_chunk(timeout)
Returns the next chunk of the http body from the socket, potentially yielding for up to timeout seconds. On error, returns nil, an error message and an error number.
stream:each_chunk()
Iterator over stream:get_next_chunk()
stream:get_body_as_string(timeout)
Reads the entire body from the stream and returns it as a string. On error, returns nil, an error message and an error number.
stream:get_body_chars(n, timeout)
Reads n characters (bytes) of body from the stream and returns them as a string. If the stream ends before n characters are read then returns the partial result. On error, returns nil, an error message and an error number.
stream:get_body_until(pattern, plain, include_pattern, timeout)
Reads in body data from the stream until the lua pattern pattern is found and returns the data as a string. plain is a boolean that indicates that pattern matching facilities should be turned off so that the function does a plain "find substring" operation, with no characters in pattern being considered magic. include_patterns specifies if the pattern itself should be included in the returned string. On error, returns nil, an error message and an error number.
stream:save_body_to_file(file, timeout)
Reads the body from the stream and saves it to the lua file handle file. On error, returns nil, an error message and an error number.
stream:get_body_as_file(timeout)
Reads the body from the stream into a temporary file and returns a lua file handle. On error, returns nil, an error message and an error number.
stream:unget(str)
Places str back on the incoming data buffer, allowing it to be returned again on a subsequent command ("un-gets" the data). Returns true on success. On error, returns nil, an error message and an error number. It's giving undo button ๐ซ
stream:write_chunk(chunk, end_stream, timeout)
Writes the string chunk to the stream. If end_stream is true, the body will be finalized and the stream will be closed. write_chunk yields indefinitely, or until timeout is exceeded. On error, returns nil, an error message and an error number.
stream:write_body_from_string(str, timeout)
Writes the string str to the stream and ends the stream. On error, returns nil, an error message and an error number.
stream:write_body_from_file(options|file, timeout)
- options is a table containing:
.file(file).count(positive integer): number of bytes of file to write, defaults to infinity (the whole file will be written)
Writes the contents of file file to the stream and ends the stream. file will not be automatically seeked, so make sure it's at the correct offset before calling. On error, returns nil, an error message and an error number.
stream:shutdown()
Closes the stream. The resources are released and the stream can no longer be used. Bye bestie ๐
๐ Modules ๐
http.bit
An abstraction layer over the various lua bit libraries.
Results are only consistent between underlying implementations when parameters and results are in the range of 0 to 0x7fffffff.
band(a, b)
Bitwise And operation.
bor(a, b)
Bitwise Or operation.
bxor(a, b)
Bitwise XOr operation.
Example
local bit = require "http.bit"
print(bit.band(1, 3)) --> 1
http.client
Deals with obtaining a connection to an HTTP server. The plug ๐.
negotiate(socket, options, timeout)
Negotiates the HTTP settings with the remote server. If TLS has been specified, this function instantiates the encryption tunnel. Parameters are as follows:
- socket is a cqueues socket object
- options is a table containing:
.tls(boolean, optional): Should TLS be used? defaults tofalse.ctx(userdata, optional): theSSL_CTX*to use if.tlsis true. If.ctxis nil then a default context will be used..sendname(string|boolean, optional): the TLS SNI host to send. defaults totruetrueindicates to copy the.hostfield as long as it is not an IPfalsedisables SNI
.version(nil|1.0|1.1|2): HTTP version to use.- nil: attempts HTTP 2 and falls back to HTTP 1.1
- 1.0
- 1.1
- 2
.h2_settings(table, optional): HTTP 2 settings to use. Seehttp.h2_connectionfor details
connect(options, timeout)
This function returns a new connection to an HTTP server. Once a connection has been opened, a stream can be created to start a request/response exchange. Please see h1_stream.new_stream and h2_stream.new_stream for more information about creating streams.
- options is a table containing the options to
http.client.negotiate, plus the following:family(integer, optional): socket family to use. defaults toAF_INEThost(string): host to connect to. may be either a hostname or an IP addressport(string|integer): port to connect to in numeric form e.g."80"or80path(string): path to connect to (UNIX sockets)v6only(boolean, optional): if theIPV6_V6ONLYflag should be set on the underlying socket.bind(string, optional): the local outgoing address and optionally port to bind in the form of"address[:port]", IPv6 addresses may be specified via square bracket notation. e.g."127.0.0.1","127.0.0.1:50000","[::1]:30000".
- timeout (optional) is the maximum amount of time (in seconds) to allow for connection to be established.
This includes time for DNS lookup, connection, TLS negotiation (if TLS enabled) and in the case of HTTP 2: settings exchange. So yeah, it's a whole saga, budget accordingly โณ.
Example
Connect to a local HTTP server running on port 8000
local http_client = require "http.client"
local myconnection = http_client.connect {
host = "localhost";
port = 8000;
tls = false;
}
http.cookie
A module for working with cookies. ๐ช (the browser kind, not the snack kind โ though honestly, both are iconic)
๐ช bake(name, value, expiry_time, domain, path, secure_only, http_only, same_site)
bestie this one literally just hands you back a string you can slap straight into a Set-Cookie header, built out of every param you passed in. that's it. that's the function. ๐
๐ parse_cookie(cookie)
takes the contents of a Cookie header (that's the cookie argument, okurr) and parses it.
returns a table where the name and value pairs are all sitting pretty as strings. โจ
๐ parse_cookies(req_headers)
same vibe, but it parses every single Cookie header inside the http.headers object you pass as req_headers.
returns a table with name and value pairs, as strings. no cap. ๐ซถ
๐ parse_setcookie(setcookie)
parses the contents of a Set-Cookie header, i.e. the setcookie you gave it. gives you back three things โ name, value and params, and here's the breakdown:
nameis a string holding the cookie namevalueis a string holding the cookie valueparamsis a table where the keys are cookie attribute names and the values are cookie attribute values
๐ช new_store()
makes you a shiny brand new cookie store. โจ
cookies are unique for a tuple of domain, path and name โ but heads up, multiple cookies with the same name can still show up in a single request because of overlapping paths or domains. so don't be gagged when you see dupes. ๐ญ
๐ store.psl
a lua-psl object you use to check stuff against the Public Suffix List. set this field to false if you want to skip checking the suffix list entirely.
defaults to the latest PSL on the system. and if lua-psl isn't installed? then it'll be nil. ๐
โฐ store.time()
a function the store calls to get the current time for expiries and such.
defaults to a function based on os.time.
๐ store.max_cookie_length
the maximum length (in bytes) of cookies in the store. this value also doubles as the default maximum cookie length for :lookup().
decreasing this value only stops new cookies from being added โ it will not remove old cookies. fr fr, don't expect it to clean up after itself.
defaults to infinity (aka no maximum size at all). ๐ฆ
๐ข store.max_cookies
the maximum number of cookies allowed in the store.
decreasing this value only prevents new cookies from being added, it will not remove old cookies.
defaults to infinity (any number of cookies is allowed). iconic behavior honestly.
๐ข store.max_cookies_per_domain
the maximum number of cookies allowed in the store per domain.
decreasing this value only prevents new cookies from being added, it will not remove old cookies.
defaults to infinity (any number of cookies is allowed). ๐
๐พ store:store(req_domain, req_path, req_is_http, req_is_secure, req_site_for_cookies, name, value, params)
tries to add a cookie to the store. let's go through the cast ๐
req_domainis the domain the cookie was obtained fromreq_pathis the path the cookie was obtained fromreq_is_httpis a boolean flag telling you if the cookie came from a โnon-HTTPโ APIreq_is_secureis a boolean flag telling you if the cookie came from a โsecureโ protocolreq_site_for_cookiesis a string with the host that should be treated as the โsite for cookiesโ (See RFC 6265bis-02 Section 5.2) โ can benilif you don't know itnameis a string with the cookie namevalueis a string with the cookie valueparamsis a table where the keys are cookie attribute names and the values are cookie attribute values
returns a boolean telling you whether a cookie actually got stored. ๐
๐พ store:store_from_request(req_headers, resp_headers, req_host, req_site_for_cookies)
tries to store any cookies it finds in the response headers.
req_headersis thehttp.headersobject for the outgoing requestresp_headersis thehttp.headersobject you got back in responsereq_hostis the host your query was pointed at (only used ifreq_headersis missing aHostheader)req_site_for_cookiesis a string with the host that should be considered the โsite for cookiesโ (See RFC 6265bis-02 Section 5.2) โ can benilif unknown
๐ store:get(domain, path, name)
returns the cookie value for the cookie stored under the domain, path and name you passed in.
๐๏ธ store:remove(domain, path, name)
deletes the cookie stored for the domain, path and name you passed.
- if
nameisnilor you just didn't pass it, then all cookies for that domain and path get removed - if
pathisnilor not passed (on top ofname), then all cookies for that domain get removed
so like... leave args off and it goes full cleanup mode. ๐
๐ store:lookup(domain, path, is_http, is_secure, is_safe_method, site_for_cookies, is_top_level, max_cookie_length)
finds the cookies that are visible and suitable for passing along to an entity.
domainis the domain that'll be sent the cookiepathis the path that'll be sent the cookieis_httpis a boolean flag indicating if the destination is a โnon-HTTPโ APIis_secureis a boolean flag indicating if the destination will be communicated with over a โsecureโ protocolis_safe_methodis a boolean flag indicating if the cookie will be sent via a safe HTTP method (See alsohttp.util.is_safe_method)site_for_cookiesis a string with the host that should be considered the โsite for cookiesโ (See RFC 6265bis-02 Section 5.2) โ can benilif unknownis_top_levelis a boolean flag indicating if this request is a โtop levelโ request (See RFC 6265bis-02 Section 5.2)max_cookie_lengthis the maximum cookie length to allow (See alsostore.max_cookie_length)
returns a string ready to drop into a Cookie header. โจ
๐ store:lookup_for_request(headers, host, site_for_cookies, is_top_level, max_cookie_length)
finds cookies suitable for adding to a request.
headersis thehttp.headersobject for the outgoing requesthostis the host your query was pointed at (only used ifheadersis missing aHostheader)site_for_cookiesis a string with the host that should be considered the โsite for cookiesโ (See RFC 6265bis-02 Section 5.2) โ can benilif unknownis_top_levelis a boolean flag indicating if this request is a โtop levelโ request (See RFC 6265bis-02 Section 5.2)max_cookie_lengthis the maximum cookie length to allow (See alsostore.max_cookie_length)
returns a string ready to drop into a Cookie header. ๐ซถ
โณ store:clean_due()
returns the number of seconds until the next cookie in the store expires.
๐งน store:clean()
removes all the expired cookies from the store. spring cleaning bestie ๐ธ
๐ store:load_from_file(file)
loads cookie data from the file object file into the store. the file needs to be in the Netscape Cookiejar format. any invalid lines in the file just get ignored, no drama.
returns true on success, or passes along nil, err, errno if a :read call fails.
๐ store:save_to_file(file)
writes the cookie data from the store into the file object file, in the Netscape Cookiejar format. note that file is not seek-ed or truncated before writing โ keep that in mind ๐
returns true on success, or passes along nil, err, errno if a :write call fails.
๐ http.h1_connection
the h1_connection module adheres to the connection interface</> and provides HTTP 1 and 1.1 specific operations.
๐ new(socket, conn_type, version)
constructor for a brand new connection. takes a cqueues socket object, a connection type string, and a numeric HTTP version number. valid values for the connection type are "client" and "server". valid values for the version number are 1 and 1.1. returns the newly initialized connection object. slay ๐
๐ h1_connection.version
specifies the HTTP version used for the connection handshake. valid values are:
1.01.1
See connection.version
๐ h1_connection:pollfd()
See connection:pollfd()
๐ h1_connection:events()
See connection:events()
๐ h1_connection:timeout()
See connection:timeout()
๐ h1_connection:connect(timeout)
See connection:connect(timeout)
๐ h1_connection:checktls()
See connection:checktls()
๐ h1_connection:localname()
See connection:localname()
๐ h1_connection:peername()
See connection:peername()
๐ h1_connection:flush(timeout)
See connection:flush(timeout)
๐ช h1_connection:shutdown(dir)
shut down is as graceful as it can possibly be: pipelined streams get shut down first, then the underlying socket gets shut down in the appropriate direction(s). so polite. ๐
dir is a string representing the direction of communication to shut down in. if it contains "r" it'll shut down reading, if it contains "w" it'll shut down writing. the default is "rw", i.e. shutting down communication in both directions.
See connection:shutdown()
๐ช h1_connection:close()
See connection:close()
๐ฑ h1_connection:new_stream()
in HTTP 1, only a client may initiate new streams with this function. server besties, sit this one out ๐
See connection:new_stream() for more information.
๐ฅ h1_connection:get_next_incoming_stream(timeout)
See connection:get_next_incoming_stream(timeout)
๐ด h1_connection:onidle(new_handler)
See connection:onidle(new_handler)
๐ h1_connection:setmaxline(read_length)
sets the maximum read buffer size (in bytes) to read_length. i.e. it sets the maximum length of lines (like headers, yk).
the default comes from the underlying socket, which grabs the (changable) cqueues default at the time of construction. that cqueues default is 4096 bytes.
๐งฝ h1_connection:clearerr(...)
clears errors so you can do further read or write operations on the connection. returns the error number of existing errors. this function is what you use to recover from known errors. ๐ซถ
๐งฝ h1_connection:error(...)
returns the error number of existing errors.
๐ค h1_connection:take_socket()
used to hand the reference of the connection socket over to another object. resets the socket to defaults and returns the single existing reference of the socket to the calling routine. you'd use this for connection upgrades, like upgrading from HTTP 1 to a WebSocket. โจ
๐ h1_connection:read_request_line(timeout)
reads a request line from the socket. returns the request method, the requested path and the HTTP version for an incoming request.
:read_request_line() yields until a "\r\n" terminated chunk is received, or until timeout is exceeded. if the incoming chunk is not a valid HTTP request line, nil is returned. on error, it returns nil, an error message, and an error number.
๐ h1_connection:read_status_line(timeout)
reads a line of input from the socket. if the input is a valid status line, you get back the HTTP version (1 or 1.1), the status code, and the reason description (if applicable).
:read_status_line() yields until a "\r\n" terminated chunk is received, or until timeout is exceeded. if the socket could not be read, it returns nil, an error message, and an error number.
๐ h1_connection:read_header(timeout)
reads a CRLF terminated HTTP header from the socket and returns the header key and value. this function will yield until a MIME compliant header item is received, or until timeout is exceeded. if the header could not be read, the function returns nil, an error, and an error message. ๐ญ
h1_connection:read_headers_done(timeout) ๐
okay so bestie this one checks for an empty line, which is the signal that the HTTP headers are done. It returns true if an empty line shows up. Any other value gets pushed back on the socket receive buffer (unget) and the function returns false. This function will yield waiting for input from the socket or until timeout is exceeded. If the socket cannot be read, it returns nil, an error and an error message.
h1_connection:read_body_by_length(len, timeout) โจ
Get len number of bytes from the socket. Use a negative number for up to that number of bytes. This function will yield and wait on the socket if the length of the buffered body is less than len. Asserts if len is not a number.
h1_connection:read_body_till_close(timeout) ๐
Reads the entire request body. This function will yield until the body is complete or timeout is expired. If the read fails the function returns nil, an error message and an error number.
h1_connection:read_body_chunk(timeout) ๐ฆ
Reads the next available line of data from the request and returns the chunk and any chunk extensions. This function will yield until chunk size is received or timeout is exceeded. If the chunk size is indicated as 0 then false and any chunk extensions are returned. Returns nil, an error message and an error number if there was an error reading the chunk header or the socket.
h1_connection:write_request_line(method, path, httpversion, timeout) ๐ธ
Writes the opening HTTP 1.x request line for a new request to the socket buffer. Yields until success or timeout. If the write fails, returns nil, an error message and an error number.
Note the request line will not be flushed to the remote server until write_headers_done is called.
h1_connection:write_status_line(httpversion, status_code, reason_phrase, timeout) ๐
Writes an HTTP status line to the socket buffer. Yields until success or timeout. If the write fails, the function returns nil, an error message and an error number.
Note the status line will not be flushed to the remote server until write_headers_done is called.
h1_connection:write_header(k, v, timeout) ๐ซถ
Writes a header item to the socket buffer as a key:value string. Yields until success or timeout. Returns nil, an error message and an error if the write fails.
Note the header item will not be flushed to the remote server until write_headers_done is called.
h1_connection:write_headers_done(timeout) ๐ญ
Terminates a header block by writing a blank line ("\r\n") to the socket. This function will flush all outstanding data in the socket output buffer. Yields until success or timeout. Returns nil, an error message and an error if the write fails.
h1_connection:write_body_chunk(chunk, chunk_ext, timeout) ๐ฅ
Writes a chunk of data to the socket. chunk_ext must be nil as chunk extensions are not supported. Will yield until complete or timeout is exceeded. Returns true on success. Returns nil, an error message and an error number if the write fails.
h1_connection:write_body_last_chunk(chunk_ext, timeout) ๐
Writes the chunked body terminator "0\r\n" to the socket. chunk_ext must be nil as chunk extensions are not supported. Will yield until complete or timeout is exceeded. Returns nil, an error message and an error number if the write fails.
Note that the connection will not be immediately flushed to the remote server; normally this will occur when trailers are written.
h1_connection:write_body_plain(body, timeout) ๐
Writes the contents of body to the socket and flushes the socket output buffer immediately. Yields until success or timeout is exceeded. Returns nil, an error message and an error number if the write fails.
http.h1_reason_phrases โญ
A table mapping from status codes (as strings) to reason phrases for HTTP 1. Any unknown status codes return "Unassigned".
Example:
local reason_phrases = require "http.h1_reason_phrases"
print(reason_phrases["200"]) --> "OK"
print(reason_phrases["342"]) --> "Unassigned"
http.h1_stream ๐
The h1_stream module adheres to the stream interface and provides HTTP 1.x specific operations.
The gzip transfer encoding is supported transparently.
h1_stream.connection ๐
See stream.connection
h1_stream.max_header_lines ๐ธ
The maximum number of header lines to read. Default is 100.
h1_stream:checktls() ๐
See stream:checktls()
h1_stream:localname() ๐ซถ
See stream:localname()
h1_stream:peername() ๐ญ
See stream:peername()
h1_stream:get_headers(timeout) ๐ฅ
See stream:get_headers(timeout)
h1_stream:write_headers(headers, end_stream, timeout) ๐
See stream:write_headers(headers, end_stream, timeout)
h1_stream:write_continue(timeout) ๐
See stream:write_continue(timeout)
h1_stream:get_next_chunk(timeout) โญ
See stream:get_next_chunk(timeout)
h1_stream:each_chunk() ๐
See stream:each_chunk()
h1_stream:get_body_as_string(timeout) ๐
See stream:get_body_as_string(timeout)
h1_stream:get_body_chars(n, timeout) ๐
See stream:get_body_chars(n, timeout)
h1_stream:get_body_until(pattern, plain, include_pattern, timeout) ๐ง
See stream:get_body_until(pattern, plain, include_pattern, timeout)
h1_stream:save_body_to_file(file, timeout) ๐
See stream:save_body_to_file(file, timeout)
h1_stream:get_body_as_file(timeout) โจ
See stream:get_body_as_file(timeout)
h1_stream:unget(str) ๐
See stream:unget(str)
h1_stream:write_chunk(chunk, end_stream, timeout) ๐
See stream:write_chunk(chunk, end_stream, timeout)
h1_stream:write_body_from_string(str, timeout) ๐
See stream:write_body_from_string(str, timeout)
h1_stream:write_body_from_file(options|file, timeout) ๐ธ
See stream:write_body_from_file(options|file, timeout)
h1_stream:shutdown() ๐
See stream:shutdown()
h1_stream:set_state(new) ๐ซถ
Sets the state of the stream to new. new must be one of the following valid states:
"open": have sent or received headers; havenโt sent body yet"half closed (local)": have sent whole body"half closed (remote)": have received whole body"closed": complete
Not all state transitions are allowed.
h1_stream:read_headers(timeout) ๐ญ
Reads and returns a header block from the underlying connection. Does not take into account buffered header blocks. On error, returns nil, an error message and an error number.
This function should rarely be used, youโre probably looking for :get_headers().
h1_stream:read_next_chunk(timeout) ๐ฅ
Reads and returns the next chunk as a string from the underlying connection. Does not take into account buffered chunks. On error, returns nil, an error message and an error number.
This function should rarely be used, youโre probably looking for :get_next_chunk().
http.h2_connection ๐
The h2_connection module adheres to the connection interface and provides HTTP 2 specific operations. An HTTP 2 connection can have multiple streams actively transmitting data at once, hence an http.h2_connection acts much like a scheduler.
new(socket, conn_type, settings) ๐
Constructor for a new connection. Takes a cqueues socket object, a connection type string and an optional table of HTTP 2 settings. Returns the newly initialized connection object in a non-connected state.
h2_connection.version โญ
Contains the HTTP connection version. Currently this will always be 2.
See connection.version.
h2_connection:pollfd() ๐
See connection:pollfd()
h2_connection:events() ๐
See connection:events()
h2_connection:timeout() ๐
See connection:timeout()
h2_connection:empty() ๐ง
h2_connection:step(timeout) ๐
h2_connection:loop(timeout) โจ
h2_connection:connect(timeout) ๐
See connection:connect(timeout)
h2_connection:checktls() ๐
See connection:checktls()
h2_connection:localname() ๐
See connection:localname()
h2_connection:peername() ๐ธ
See connection:peername()
h2_connection:flush(timeout) ๐
See connection:flush(timeout)
h2_connection:shutdown() ๐ซถ
See connection:shutdown()
h2_connection:close() ๐ญ
See connection:close()
h2_connection:new_stream(id) ๐ฅ
Create and return a new h2_stream. id (optional) is the stream id to assign the new stream, if not specified for client initiated streams this will be the next free odd numbered stream, for server initiated streams this will be the next free even numbered stream.
See connection:new_stream() for more information.
h2_connection:get_next_incoming_stream(timeout) ๐
See connection:get_next_incoming_stream(timeout)
h2_connection:onidle(new_handler) ๐
See connection:onidle(new_handler)
h2_connection:read_http2_frame(timeout) โญ
h2_connection:write_http2_frame(typ, flags, streamid, payload, timeout, flush) ๐
h2_connection:ping(timeout) ๐
h2_connection:write_window_update(inc, timeout) ๐
h2_connection:write_goaway_frame(last_stream_id, err_code, debug_msg, timeout) ๐ง
h2_connection:set_peer_settings(peer_settings) ๐
h2_connection:ack_settings() โจ
h2_connection:settings(tbl, timeout) ๐
http.h2_error ๐
A type of error object that encapsulates HTTP 2 error information. An http.h2_error object has fields:
name: The error name: a short identifier for this errorcode: The error codedescription: The description of the error codemessage: An error messagetraceback: A traceback taken at the point the error was thrownstream_error: A boolean that indicates if this is a stream level or protocol level error
errors ๐
A table containing errors as defined by the HTTP 2 specification. It can be indexed by error name (e.g. errors.PROTOCOL_ERROR) or numeric code (e.g. errors[0x1]).
is(ob) ๐ธ
Returns a boolean indicating if the object ob is an http.h2_error object.
h2_error:new(ob) ๐
Creates a new error object from the passed table. The table should have the form of an error object i.e. with fields name, code, message, traceback, etc.
Fields name, code and description are inherited from the parent h2_error object if not specified.
stream_error defaults to false.
h2_error:new_traceback(message, stream_error, lvl) ๐ซถ
Creates a new error object, recording a traceback from the current thread.
h2_error:error(message, stream_error, lvl) ๐ญ
Creates and throws a new error.
h2_error:assert(cond, ...) ๐ฅ
If cond is truthy, returns cond, ...
If cond is falsy (i.e. false or nil), throws an error with the first element of ... as the message.
http.h2_stream ๐
An h2_stream represents an HTTP 2 stream. The module follows the stream interface as well as HTTP 2 specific functions.
h2_stream.connection ๐
See stream.connection
h2_stream:checktls() โญ
See stream:checktls()
h2_stream:localname() ๐
See stream:localname()
h2_stream:peername() ๐
See stream:peername()
h2_stream:get_headers(timeout) ๐
See stream:get_headers(timeout)
h2_stream:write_headers(headers, end_stream, timeout) ๐ง
See stream:write_headers(headers, end_stream, timeout)
h2_stream:write_continue(timeout) ๐
See stream:write_continue(timeout)
h2_stream:get_next_chunk(timeout)
bestie this one is a rerun ๐ she's literally stream:get_next_chunk(timeout) in a different outfit. Go read that one.
h2_stream:each_chunk()
same deal โ it's stream:each_chunk(). periodt.
h2_stream:get_body_as_string(timeout)
yep, stream:get_body_as_string(timeout). copy paste behaviour, no cap.
h2_stream:get_body_chars(n, timeout)
that's stream:get_body_chars(n, timeout) ๐
h2_stream:get_body_until(pattern, plain, include_pattern, timeout)
yup: stream:get_body_until(pattern, plain, include_pattern, timeout).
h2_stream:save_body_to_file(file, timeout)
stream:save_body_to_file(file, timeout) โ go look it up queen ๐ธ
h2_stream:get_body_as_file(timeout)
stream:get_body_as_file(timeout). iconic, moving on.
h2_stream:unget(str)
stream:unget(str). that's the whole thing.
h2_stream:write_chunk(chunk, end_stream, timeout)
stream:write_chunk(chunk, end_stream, timeout) โจ
h2_stream:write_body_from_string(str, timeout)
stream:write_body_from_string(str, timeout).
h2_stream:write_body_from_file(options|file, timeout)
stream:write_body_from_file(options|file, timeout).
h2_stream:shutdown()
stream:shutdown(). same energy, same everything.
okay so the next little cluster? the docs just list these with zero explanation. no notes, no description, nothing. she's mysterious, and honestly? respect ๐
h2_stream:pick_id(id)โ picks an id. that's all we're given, fr fr.h2_stream:set_state(new)โ sets the state. that's it, that's the tweet.h2_stream:reprioritise(child, exclusive)โ reprioritiseschildwithexclusivein the mix. again: no elaboration. it's giving enigma.
h2_stream:write_http2_frame(typ, flags, payload, timeout, flush)
writes a frame with h2_stream's stream id ๐ฆ
and then: go see h2_connection:write_http2_frame(typ, flags, streamid, payload, timeout, flush).
and here come the rest of the frame-writing girlies โ again, listed bare, no commentary in the source, so I'm not gonna invent any:
h2_stream:write_data_frame(payload, end_stream, padded, timeout, flush)h2_stream:write_headers_frame(payload, end_stream, end_headers, padded, exclusive, stream_dep, weight, timeout, flush)h2_stream:write_priority_frame(exclusive, stream_dep, weight, timeout, flush)h2_stream:write_rst_stream_frame(err_code, timeout, flush)h2_stream:rst_stream(err, timeout)h2_stream:write_settings_frame(ACK, settings, timeout, flush)h2_stream:write_push_promise_frame(promised_stream_id, payload, end_headers, padded, timeout, flush)
h2_stream:push_promise(headers, timeout)
pushes a new promise to the client ๐ซถ
returns the new stream as a h2_stream.
h2_stream:write_ping_frame(ACK, payload, timeout, flush)h2_stream:write_goaway_frame(last_streamid, err_code, debug_msg, timeout, flush)h2_stream:write_window_update_frame(inc, timeout, flush)h2_stream:write_window_update(inc, timeout)h2_stream:write_continuation_frame(payload, end_headers, timeout, flush)
๐ http.headers
an ordered list of header fields, bestie. every field has a name, a value and a never_index flag โ and that flag is the one that says "hey this is potentially sensitive data, don't index it" ๐
every headers object also keeps an index by field name so you can grab values by key fast. but heads up: there can totally be multiple values for a given field name โ like an HTTP server sending two Set-Cookie headers. so don't act surprised ๐
and like we said back in the Conventions section: HTTP 1 request and status line fields get passed around inside headers objects under the keys ":authority", ":method", ":path", ":scheme" and ":status", exactly as defined in HTTP 2. which means they're all kept in string form. important to remember for ":status", okay? it's a string. not a number. a string. ๐
new()
creates and returns a brand new headers object. slay.
headers:len()
returns the number of headers.
also available as #headers in Lua 5.2+. ๐
headers:clone()
creates and returns a clone of the headers object. twin behaviour.
headers:append(name, value, never_index)
append a header, bestie.
nameis the header field name. lower case is the convention. it will not be validated at this time.valueis the header field value. it will not be validated at this time either. (yes, really. no gatekeeping here.)never_indexis an optional boolean that says whether the value should be considered secret. it defaults totruefor these header fields:authorization,proxy-authorization,cookieandset-cookie.
headers:each()
an iterator over all the headers that emits name, value, never_index.
Example
local http_headers = require "http.headers"
local myheaders = http_headers.new()
myheaders:append(":status", "200")
myheaders:append("set-cookie", "foo=bar")
myheaders:append("connection", "close")
myheaders:append("set-cookie", "baz=qux")
for name, value, never_index in myheaders:each() do
print(name, value, never_index)
end
--[[ prints:
":status", "200", false
"set-cookie", "foo=bar", true
"connection", "close", false
"set-cookie", "baz=qux", true
]]
headers:has(name)
returns a boolean saying whether the headers object has a field with the given name. vibe check, but for headers.
headers:delete(name)
removes all occurrences of a field name from the headers object. every single one. gone. ๐
headers:geti(i)
return the i-th header as name, value, never_index.
headers:get_as_sequence(name)
returns all headers with the given name in a table. that table will contain a field .n holding the number of elements. โจ
Example
local http_headers = require "http.headers"
local myheaders = http_headers.new()
myheaders:append(":status", "200")
myheaders:append("set-cookie", "foo=bar")
myheaders:append("connection", "close")
myheaders:append("set-cookie", "baz=qux")
local mysequence = myheaders:get_as_sequence("set-cookie")
--[[ mysequence will be:
{n = 2; "foo=bar"; "baz=qux"}
]]
headers:get(name)
returns all headers with the given name as multiple return values. yes, plural. she's generous.
headers:get_comma_separated(name)
returns all headers with the given name as items in a comma separated string. ๐
headers:modifyi(i, value, never_index)
change the i-th's header to a new value and never_index. glow-up for one specific header.
headers:upsert(name, value, never_index)
if a header with the given name already exists, replace it. if not, append it to the list of headers.
cannot be used when a header name already has multiple values. ๐ซ so don't try it, she will not cooperate.
headers:sort()
sorts the list of headers by their field name, ordering the ones starting with : first. if names are equal, then it sorts by value, then by never_index. so organised. so main character.
headers:dump(file, prefix)
prints the headers list to the given file, one per line. if file isn't given, it prints to stderr instead. prefix gets prefixed to each line. ๐ธ
๐ http.hpack
okay this whole section is just a big list of signatures with no explanations attached. so I'm handing them to you exactly as they are, no invented lore, no extra claims:
new(SETTINGS_HEADER_TABLE_SIZE)hpack_context:append_data(val)hpack_context:render_data()hpack_context:clear_data()hpack_context:evict_from_dynamic_table()hpack_context:dynamic_table_tostring()hpack_context:set_max_dynamic_table_size(SETTINGS_HEADER_TABLE_SIZE)hpack_context:encode_max_size(val)hpack_context:resize_dynamic_table(new_size)hpack_context:add_to_dynamic_table(name, value, k)hpack_context:dynamic_table_id_to_index(id)hpack_context:lookup_pair_index(k)hpack_context:lookup_name_index(name)hpack_context:lookup_index(index)hpack_context:add_header_indexed(name, value, huffman)hpack_context:add_header_never_indexed(name, value, huffman)hpack_context:encode_headers(headers)hpack_context:decode_headers(payload, header_list, pos)
that's the whole list, bestie. every single one. no skipping ๐
๐ http.hsts
data structures useful for HSTS (HTTP Strict Transport Security). the security girlies are gonna eat this up.
new_store()
creates and returns a new HSTS store. fresh out the box โจ
hsts_store.max_items
the maximum number of items allowed in the store. decreasing this value will only prevent new items from being added โ it will not remove old items. so don't think lowering it cleans anything up for you, it doesn't ๐ญ
defaults to infinity (meaning any number of items is allowed).
hsts_store:clone()
creates and returns a copy of a store.
hsts_store:store(host, directives)
add new directives to the store about the given host. directives should be a table of directives, and it must include the key "max-age".
returns a boolean indicating if the item was accepted. ๐ซถ
hsts_store:remove(host)
removes the entry for host from the store (if it exists). if it doesn't exist, whatever, nothing happens.
hsts_store:check(host)
returns a boolean indicating if the given host is a known HSTS host. ๐
hsts_store:clean_due()
returns the number of seconds until the next item in the store expires. tick tock โณ
hsts_store:clean()
removes expired entries from the store. self-care.
๐ฆ http.proxies
new()
returns an empty proxies object. a blank canvas, bestie.
proxies:update(getenv)
getenv defaults to os.getenv.
reads environmental variables that are used to control if requests go through a proxy:
http_proxy(orCGI_HTTP_PROXYif you're running in a program withGATEWAY_INTERFACEset): the proxy to use for normal HTTP connections.https_proxyorHTTPS_PROXY: the proxy to use for HTTPS connections.all_proxyorALL_PROXY: the proxy to use for all connections โ overridden by the other options though.no_proxyorNO_PROXY: a list of hosts to not use a proxy for.
returns proxies. ๐
proxies:choose(scheme, host)
returns the proxy to use for the given scheme and host as a URI. picks your fighter for you.
๐ http.request
the http.request module encapsulates all the functionality required to retrieve an HTTP document from a server. this is the whole point of the operation, honestly.
new_from_uri(uri)
creates a new http.request object from the given uri.
new_connect(uri, connect_authority)
creates a new http.request object from the given uri that will perform a CONNECT request. fancy.
request.host
the host this request should be sent to.
request.port
the port this request should be sent to.
request.bind
the local outgoing address and optionally port to bind, in the form of "address[:port]". default is to let the kernel choose an address+port. let the kernel cook ๐ณ
IPv6 addresses may be specified via square bracket notation. e.g. "127.0.0.1", "127.0.0.1:50000", "[::1]:30000".
this option is rarely needed. supplying an address can be used to manually select the network interface to make the request from, while supplying a port is only really used to interoperate with firewalls or devices that demand use of a certain port.
request.tls
a boolean indicating if TLS should be used.
request.ctx
an alternative SSL_CTX* to use. if it's not specified, it uses the default TLS settings (see http.tls for information).
request.sendname
the TLS SNI host name used.
request.version
the HTTP version to use; leave it as nil to auto-select. she'll pick for you, trust her.
request.proxy
specifies the proxy that the request will be made through. the value should be a URI or false to turn off proxying for the request. ๐ซ
request.headers
a http.headers object of headers that will be sent in the request.
request.hsts
the http.hsts store that will be used to enforce HTTP strict transport security. an attempt will be made to add strict transport headers from a response to the store.
defaults to a shared store.
request.proxies
the http.proxies object used to select a proxy for the request. only consulted if request.proxy is nil. ๐
request.cookie_store
the http.cookie.store that will be used to find cookies for the request. an attempt will be made to add cookies from a response to the store.
defaults to a shared store.
request.is_top_level
a boolean flag indicating if this request is a "top level" request (See RFC 6265bis-02 Section 5.2).
defaults to true.
request.site_for_cookies
a string containing the host that should be considered as the "site for cookies" (See RFC 6265bis-02 Section 5.2). can be nil if it's unknown. ๐
- Defaults to
nil. ๐ค (yeah this one's just chillin' with no value set, bestie)
request.follow_redirects ๐
Boolean that says whether :go() should chase down redirects like it's stalking an ex's finsta. Defaults to true.
request.expect_100_timeout โณ
The number of seconds to wait for a 100 Continue response before just going ahead and sending the request body anyway. Defaults to 1.
request.max_redirects ๐
The max number of redirects to follow before giving up on life. Defaults to 5. Set it to math.huge if you're built different and refuse to ever give up.
request.post301 ๐ฎ
Respect RFC 2616 Section 10.3.2 and don't convert POST requests into body-less GET requests when following a 301 redirect. The non-RFC behaviour is, like, everywhere in web browsers and servers just assume it. Modern HTTP endpoints send status code 308 to say "hey, don't change my method." Defaults to false.
request.post302 ๐ฎ
Respect RFC 2616 Section 10.3.3 and don't convert POST requests into body-less GET requests when following a 302 redirect. Same deal โ the non-RFC behaviour is ubiquitous in browsers and assumed by servers. Modern HTTP endpoints send status code 307 to indicate they don't want the method changed. Defaults to false.
request:clone() ๐งฌ
Creates and returns a clone of the request. Yes, it's giving identical twin energy.
The clone has its own deep copies of the .headers and .h2_settings fields.
The .tls and .body fields are shallow copied from the original request.
request:handle_redirect(headers) ๐ฆ
Process a redirect.
headers should be response headers for a redirect.
Returns a new request object that will fetch from the new location.
request:to_uri(with_userinfo) ๐ชช
Returns a URI for the request.
If with_userinfo is true and the request has an authorization header (or proxy-authorization for a CONNECT request), the returned URI will contain a userinfo component.
request:set_body(body) ๐ฆ
Lets you set a request body. body may be a string, function or lua file object.
- If
bodyis a string it will be sent as given. - If
bodyis a function, it will be called repeatedly like an iterator. It should return chunks of the request body as a string ornilif done. - If
bodyis a lua file object, it will be:seek'd to the start, then sent as a body. Any errors encountered during file operations will be thrown.
request:go(timeout) ๐
Performs the request. The moment we've all been waiting for, fr fr.
The request object is not invalidated; and can be reused for a new request. On success, returns the response headers and a stream.
๐ http.server ๐
http.server objects are used to encapsulate the accept() and dispatch of http clients. Each new client request will invoke the onstream callback in a new cqueues managed coroutine. In addition to constructing and returning a HTTP response, an onstream handler may decide to take ownership of the connection for other purposes, e.g. upgrade from a HTTP 1.1 connection to a WebSocket connection. ๐
For examples of how to use the server library, please see the examples directory in the source tree.
new(options) โจ
Creates a new instance of an HTTP server listening on the given socket.
.socket(cqueues.socket): the socket thataccept()will be called on.onerror(function): Function that will be called when an error occurs (default handler throws an error). Seeserver:onerror().onstream(function): Callback function for handling a new client request. The function receives the server and the new stream as parameters. If the callback throws an error it will be reported from:step()or:loop().tls(boolean): Specifies if the system should use Transport Layer Security. Values are:nil: Allow both tls and non-tls connectionstrue: Allows tls connections onlyfalse: Allows non-tls connections only
.ctx(context object): Anopenssl.ssl.contextobject to use for tls connections. Ifnilis passed, a self-signed context will be generated..connection_setup_timeout(number): Timeout (in seconds) to wait for client to send first bytes and/or complete TLS handshake. Default is10seconds..intra_stream_timeout(number): Timeout (in seconds) to wait for a new stream on an idle connection before giving up and closing the connection.version(number): The http version to allow to connect (default: any).cq(cqueue): A cqueues controller to use as a main loop. The default is a new controller for the server..max_concurrent(number): Maximum number of connections to allow live at a time. Default is infinity. โพ๏ธ
listen(options) ๐ง
Creates a new socket and returns an HTTP server that will accept() from it. Parameters are the same as new(options) except instead of .socket you provide the following:
.host(string): Local IP address in dotted decimal or IPV6 notation. This value is required if.pathis not specified..port(number): IP port for the local socket. Specify0for automatic port selection. Ports 1-1024 require the application has root privilege to run. Maximum value is65535. If.tls == nilthen this value is required. Otherwise, the defaults are:80if.tls == false443if.tls == true
.path(string): Path to UNIX a socket. This value is required if.hostis not specified..family(string): Protocol family. Default is"AF_INET".v6only(boolean): Specifytrueto limit all connections to ipv6 only (no ipv4-mapped-ipv6). Default isfalse..mode(string):fchmodorchmodsocket after creating UNIX domain socket..mask(boolean): Set and restore umask when binding UNIX domain socket..unlink(boolean):truemeans unlink socket path before binding..reuseaddr(boolean): Turn onSO_REUSEADDRflag..reuseport(boolean): Turn onSO_REUSEPORTflag.
server:onerror(new_handler) ๐จ
If called with parameters, the function replaces the current error handler function with new_handler and returns a reference to the old function. Calling the function with no parameters returns the current error handler. The default handler throws an error. The onerror function for the server can be set during instantiation through the options table passed to the server.listen(options) function.
server:listen(timeout) ๐
Initializes the server socket and if required, resolves DNS. server:listen() is required if localname is called before step or loop. On error, returns nil, an error message and an error number.
server:localname() ๐
Returns the connection information for the local socket. Returns address family, IP address and port for an external socket. For Unix domain sockets, the function returns AF_UNIX and the path. If the connection object is not connected, returns AF_UNSPEC (0). On error, returns nil, an error message and an error number.
server:pause() โธ๏ธ
Cause the server loop to stop processing new clients until resume is called. Existing client connections will run until closed.
server:resume() โถ๏ธ
Resumes a paused server and processes new client requests. Welcome back bestie ๐ซถ
server:close() ๐
Shutdown the server and close the socket. A closed server cannot be reused. That's it, it's over, no coming back from this one ๐
server:pollfd() ๐
Returns a file descriptor (as an integer) or nil.
The file descriptor can be passed to a system API like select or kqueue to wait on anything this server object wants to do. This method is used for integrating with other main loops, and should be used in combination with :events() and :timeout().
server:events() ๐๏ธ
Returns a string indicating the type of events the object is waiting on: the string will contain "r" if it wants to be steped when the file descriptor returned by pollfd() has had POLLIN indicated; "w" for POLLOUT or "p" for POLLPRI.
This method is used for integrating with other main loops, and should be used in combination with :pollfd() and :timeout().
server:timeout() โฐ
The maximum time (in seconds) to wait before calling server:step().
This method is used for integrating with other main loops, and should be used in combination with :pollfd() and :events().
server:empty() ๐ซ
Returns true if the master socket and all client connection have been closed, false otherwise.
server:step(timeout) ๐ฃ
Step once through server's main loop: any waiting clients will be accept()-ed, any pending streams will start getting processed, and each onstream handler will get be run at most once. This method will block for up to timeout seconds. On error, returns nil, an error message and an error number.
This can be used for integration with external main loops.
server:loop(timeout) ๐
Run the server as a blocking loop for up to timeout seconds. The server will continue to listen and accept client requests until either :pause() or :close() is called, or an error is experienced. Just vibing in the loop forever, honestly.
server:add_socket(socket) ๐
Add a new connection socket to the server for processing. The server will use the current onstream request handler and all options currently specified through the server.listen(options) constructor. add_socket can be used to process connection sockets obtained from an external source such as:
- Another cqueues thread with some other master socket.
- From
inetdfor start on demand daemons. - A Unix socket with
SCM_RIGHTS.
server:add_stream(stream) ๐
Add an existing stream to the server for processing.
๐งฆ http.socks ๐งฆ
Implements a subset of the SOCKS proxy protocol.
connect(uri) ๐
uri is a string with the address of the SOCKS server. A scheme of "socks5" will resolve hosts locally, a scheme of "socks5h" will resolve hosts on the SOCKS server. If the URI has a userinfo component it will be sent to the SOCKS server as a username and password.
Returns a http.socks object.
fdopen(socket) ๐ช
This function takes an existing cqueues.socket as a parameter and returns a http.socks object with socket as its base.
socks.needs_resolve ๐
Specifies if the destination host should be resolved locally.
socks:clone() ๐งฌ
Make a clone of a given socks object.
socks:add_username_password_auth(username, password) ๐
Add username + password authorisation to the set of allowed authorisation methods with the given credentials.
socks:negotiate(host, port, timeout) ๐ค
Complete the SOCKS connection.
Negotiates a socks connection. host is a required string passed to the SOCKS server as the host address. The address will be resolved locally if .needs_resolve is true. port is a required number to pass to the SOCKS server as the connection port. On error, returns nil, an error message and an error number.
socks:close() ๐ช
socks:take_socket() ๐ซด
Take possession of the socket object managed by the http.socks object. Returns the socket (or nil if not available).
๐ http.tls ๐
has_alpn ๐
Boolean indicating if ALPN is available in the current environment.
It may be disabled if OpenSSL was compiled without ALPN support, or is an old version. ๐ญ
has_hostname_validationโ a boolean that tells you whether hostname validation is available in the environment you're running in right now. It might be switched off if your OpenSSL is an old version. ๐ญmodern_cipher_listโ the Mozilla โModernโ cipher list, all strung together as a colon separated list, ready to be passed straight to OpenSSL. ๐intermediate_cipher_listโ same energy, but the Mozilla โIntermediateโ cipher list: colon separated, OpenSSL-ready, no notes.old_cipher_listโ and the Mozilla โOldโ cipher list, also colon separated, also ready to pass to OpenSSL. (the vintage collection fr ๐ซถ)banned_ciphersโ a set (a table with string keys and values oftrue) holding the ciphers that are banned in HTTP 2, where the keys are OpenSSL cipher names. Ciphers OpenSSL doesn't know about are justโฆ not in the set. ๐new_client_context()โ creates and returns a brand new luaossl SSL context, and it's genuinely useful for HTTP client connections. main character energy.new_server_context()โ creates and returns a fresh luaossl SSL context, this one useful for HTTP server connections. ๐
๐งฐ http.util ๐งฐ
encodeURI(str),encodeURIComponent(str),decodeURI(str),decodeURIComponent(str)โ all four of these are listed completely bare in the source, with zero description attached, so I'm not about to invent one for you. The signatures are the whole story here. ๐query_args(str)โ hands you back an iterator over the pairs insidestr.
Example:
local http_util = require "http.util"
for name, value in http_util.query_args("foo=bar&baz=qux") do
print(name, value)
end
--[[ prints:
"foo", "bar"
"baz", "qux"
]]
dict_to_query(dict)โ takes a dictionary (a table with string keys) whose values are strings, and converts it into an encoded query string. โจ
Example:
local http_util = require "http.util"
print(http_util.dict_to_query({foo = "bar"; baz = "qux"})) --> "baz=qux&foo=bar"
resolve_relative_path(orig_path, relative_path)โ listed with no blurb in the source, bestie. Signature only, that's all she wrote. ๐is_safe_method(method)โ returns a boolean telling you whether the stringmethodyou passed in is a โsafeโ method. Peep RFC 7231 section 4.2.1 for more. ๐ธis_ip(str)โ returns a boolean telling you whether the stringstris a valid IP.scheme_to_portโ a map from schemes (as strings) to default ports (as integers).split_authority(authority, scheme)โ splits an authority into its host and port components. If the authority doesn't have a port component, it'll attempt to use the default for the scheme. ๐ซ
Example:
local http_util = require "http.util"
print(http_util.split_authority("localhost:8000", "http")) --> "localhost", 8000
print(http_util.split_authority("example.com", "https")) --> "localhost", 443
to_authority(host, port, scheme)โ joins the host and port together to make a valid authority component. It omits the port if that port is the default for the scheme.imf_date(time)โ returns the time in HTTP's preferred date format (see RFC 7231 section 7.1.1.1).timedefaults to the current time. ๐ฐ๏ธmaybe_quote(str)โ the little decision tree, and it goes like this:- If
stris a valid token, return it as-is. - If
strwould be valid as a quoted-string, return the quoted version. - Otherwise, returns
nil.
- If
๐ http.version ๐
nameโ"lua-http". That's it. That's the name. ๐versionโ the current version of lua-http, as a string.
๐ธ๏ธ http.websocket ๐ธ๏ธ
new_from_uri(uri, protocols)โ creates a newhttp.websocketobject of type"client"from the URI you give it.protocols(optional) should be a lua table containing a sequence of protocols to send to the server. ๐
new_from_stream(stream, headers)โ attempts to create a newhttp.websocketobject of type"server"from the given request headers and stream.streamshould be a live HTTP 1 stream of the"server"type.headersshould be headers of a suspected websocket upgrade request from an HTTP 1 client.- This function has no side effects, which is exactly why it's okay to use it tentatively. ๐
websocket.close_timeoutโ the amount of time (in seconds) to wait between sending a close frame and actually closing the connection. Defaults to3seconds.websocket:accept(options, timeout)โ completes negotiation with a websocket client.optionsis a table containing:headers(optional) โ a headers object to use as a prototype for the response headers.protocols(optional) โ should be a lua table containing a sequence of protocols to allow from the client.
- Usually called after a successful
new_from_stream.
websocket:connect(timeout)โ connect to a websocket server. Usually called after a successfulnew_from_uri. ๐websocket:receive(timeout)โ reads and returns the next data frame plus its opcode. Any ping frames that show up while it's reading get responded to automatically. ๐- The opcode
0x1will be returned as"text"and0x2will be returned as"binary".
- The opcode
websocket:each()โ an iterator overwebsocket:receive().websocket:send_frame(frame, timeout)โ the low level function for sending a raw frame.websocket:send(data, opcode, timeout)โ sends the given data as a data frame.datashould be a string.opcodecan be a numeric opcode,"text"or"binary". If it'snil, it defaults to a text frame. Heads up: this opcode is the websocket frame opcode, not an application specific opcode. It should be one from the IANA registry.
websocket:send_ping(data, timeout)โ sends a ping frame.datais optional.
websocket:send_pong(data, timeout)โ sends a pong frame. Works as a unidirectional keep-alive.datais optional.
websocket:close(code, reason, timeout)โ closes the websocket connection.codedefaults to1000.reasonis an optional string.
๐๏ธ http.zlib ๐๏ธ
An abstraction layer sitting over the various lua zlib libraries. ๐ง
engineโ currently either"lua-zlib"or"lzlib".inflate()โ returns a closure that inflates (uncompresses) a zlib stream.- That closure takes a string of compressed data and an end of stream flag (boolean) as parameters, and returns the inflated output as a string. It will throw an error if the input isn't a valid zlib stream. โ ๏ธ
deflate()โ returns a closure that deflates (compresses) a zlib stream.- That closure takes a string of uncompressed data and an end of stream flag (boolean) as parameters, and returns the deflated output as a string.
Example:
local zlib = require "http.zlib"
local original = "the racecar raced around the racecar track"
local deflater = zlib.deflate()
local compressed = deflater(original, true)
print(#original, #compressed) -- compressed should be smaller
local inflater = zlib.inflate()
local uncompressed = inflater(compressed, true)
assert(original == uncompressed)
๐ http.compat.prosody ๐
Gives you usage that's similar to prosody's net.http.
request(url, ex, callback)โ a few key differences from the prosodynet.http.request:- it must be called from within a running cqueue
- the callback may be called from a different thread in the cqueue
- the returned object will be a
http.requestobject- this object is passed to the callback on errors, and as the fourth argument on success
- the default user-agent will be from lua-http (rather than
"Prosody XMPP Server") - lua-http features (such as HTTP2) will be used where possible
Example:
local prosody_http = require "http.compat.prosody"
local cqueues = require "cqueues"
local cq = cqueues.new()
cq:wrap(function()
prosody_http.request("http://httpbin.org/ip", {}, function(b, c, r)
print(c) --> 200
print(b) --> {"origin": "123.123.123.123"}
end)
end)
assert(cq:loop())
๐ http.compat.socket ๐
Gives you compatibility with luasocket's http.request module.
Differences:
- it will automatically be non-blocking when it's run inside a cqueues managed coroutine
- lua-http features (such as HTTP2) will be used where possible
Example โ using the simple interface as part of a normal script:
local socket_http = require "http.compat.socket"
local body, code = assert(socket_http.request("http://lua.org"))
print(code, #body) --> 200, 2514
โจ Links โจ
- Github
- Issue tracker
๐ AUTHORS ๐
- Daurnimator
quae@daurnimator.com
and that's a wrap on the whole thing, bestie ๐ซถ the footer down there reads lua-http() โ source and version, baby โ with the date stamped right beside it on the left, so go peek at your own copy for the exact stamp. you ate this entire page. periodt. ๐
โจ
๐ฆ where this page comes from
- fedora 43: lua-http 0.3-18.fc43 ยท MIT
- fedora 43: lua5.1-http 0.3-18.fc43 ยท MIT
- fedora 44: lua-http 0.3-19.fc44 ยท MIT
- fedora 44: lua5.1-http 0.3-19.fc44 ยท MIT
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms