Core: Hub
Coroutines
- h:sleep(ms)
- Suspends the current green thread until at least
msmilliseconds in the future, when it will be resumed. - h:spawn(f)
- Spawns and queues to run the callable
fas a new green thread. The current thread will yield to be resumed after 1 tick of the event loop. - h:spawn_later(ms, f)
- Schedules the callable
fto run at leastmsmilliseconds in the future, in a new green thread.
local h = require("levee").Hub()
h:spawn(function()
while true do
print("tick")
h:sleep(1000)
end
end)
h:sleep(500)
while true do
print("tock")
h:sleep(1000)
end
IO
- h.io:stdin()
- Returns an io.R object to work with this processes stdin
local h = require("levee").Hub()
local stdin = h.io:stdio()
local stream = stdin:stream()
while true do
local err, line = stream:line()
if err then break end
print(line)
end
- h.io:stdout()
- Returns an io.W object to work with this processes stdout
Network
- h.stream:dial(port, [host] | uri | options)
- Establishes a streamed network connection based on the supplied
portandhost(host defaults to 'localhost') auristring (for example 'https://foo.com') oroptions. Returnserr, io.RW
Options are:
-
unix: path to unix domain socket (TBD) OR -
port: port to connect to -
host: host to connect to (default: localhost) -
timeout: timeout for reads and writes on this connection -
connect_timeout: timeout to apply to connection -
tls: upgrade this connection to use tls. Notetimeoutalso applies to the tls handshake. The value is a table of TLS Options.
- h.stream:listen([port, [host]] | options)
- Binds to
portandhost(port defaults to 0, host defaults to 'localhost') or as specified withoptionsand listens for streamed connections. Returnserr, Recver. The Recver yieldserr, io.RW for each accepted connection.
Options are:
-
unix: path to unix domain socket (TBD) OR -
port: port to bind to (default: 0) -
host: host to bind to (default: localhost) -
backlog: the maximum length for the queue of pending connections (default: 256) -
timeout: sets the read / write timeout for accepted connections -
tls: upgrade accepted connections to use tls. Notetimeoutalso applies to the tls handshake. The value is a table of TLS Options.
local h = require("levee").Hub()
-- a basic echo server
local err, serve = h.stream:listen({port=9000})
while true do
local err, conn = serve:recv()
if err then break end
h:spawn(function()
local buf = levee.d.Buffer(4096)
conn:readinto(buf:tail())
conn:write(buf:value())
conn:close()
end)
end
Process
- h.process:spawn(path, [options])
- Spawns the binary specified by
path, and returns achildobject to interact with the child process.
Options are:
-
argv: list of command line arguments -
io: a table that describes how to treat the child process' IO. By default theSTDIN,STDOUTandSTDERRof the child process are captured and available to the parent process, for writing and reading, through the variablesstdin,stdoutandstderr. Alternatively, it's possible to assign the child'sSTDIN,STDOUTandSTDERRto a specific file descriptor. By settingSTDIN = 0,STDOUT = 1orSTDERR = 2, the child'sstdin,stdoutorstderrwill be left unchanged. Addtionally, it is possible to assign an arbitrary file descriptor to another file descriptor. This is useful when the child wants to write to the parent without usingSTDOUTorSTDERR. In this case, the child must know the value of the writing file descriptor ahead of time.
local levee = require("levee")
local _ = levee._
local h = levee.Hub()
local parent_r, parent_w = _.pipe()
local stdout_r, stdout_w = _.pipe()
local cmd_fd_no = 1020
local cmd_fd_path = _.path.join("/dev/fd", tostring(cmd_fd_no))
local cmd = "tee"
local cmd_args = {cmd_fd_path}
local io_args = {[cmd_fd_no]=parent_w, STDOUT=stdout_w}
local child = h.process:spawn(cmd, {argv=cmd_args, io=io_args})
child.stdin:write("foo")
print(_.reads(parent_r)) -- foo
print(_.reads(stdout_r)) -- foo
child.stdin:close()
child.done:recv()
Protocol Conveniences
HTTP
Client side methods
- .p.http:write_request(method, path, params, headers, body)
- writes a HTTP Request. Returns
err. - .p.http:read_response()
- read a HTTP Response. Returns
err,res.
local err = conn.p.http:write_request("GET", "/foo", {a = "b"})
local err, res = conn.p.http:read_response()
- .p.http:get(path, [options])
- convenience to write a GET Request and read it's
Response. Returns
err,res.optionsshould be a Lua table with optional attributes:params,headers. - .p.http:head(path, [options])
- convenience to write a HEAD Request and read it's
Response. Returns
err,res.optionsshould be a Lua table with optional attributes:params,headers. - .p.http:post(path, [options])
- convenience to write a POST Request and read it's
Response. Returns
err,res.optionsshould be a Lua table with optional attributes:params,headers,data - .p.http:put(path, [options])
- convenience to write a PUT Request and read it's
Response. Returns
err,res.optionsshould be a Lua table with optional attributes:params,headers,data
Serve side methods
- .p.http:read_request()
- read a HTTP Request. Returns
err,req. The.httpattribute is directly callable to provide an iterator convenience for yielding all HTTP Request for a connection - .p.http::write_response(status, headers, body)
- write a HTTP Response.
statuscan be number or alevee.p.http.status.headerscan be a Lua table or a d.Map.bodycan be a number or a string, to indicate this is a Content-Length response. If it's a number the caller is responsible for writing that many bytes. Ifbodyisnilit indicates this response isTransfer-Encoding: chunked. The caller should use successive:write_chunkto write the body. Returnserr.
local err, req = conn.p.http:read_request() -- or
for req in conn.p.http do
conn.p.http:write_response(200, nil, "Hello")
end
- .p.http::write_chunk(chunk)
- writes a chunk.
chunkcan be a number or a string. If it's a number the caller is responsible for writing that many bytes. Ifchunkisnilit indicates this is the final chunk of the body. Returnserr.
Objects: IO
io.R
- r:read(char*, len)
- Reads up to
lenbytes intochar*. Returnserr,nwherenis the number of bytes actually read - r:readn(char*, n, [len])
- Reads at least
nbytes intochar*, but no more thenlen.lendefaults ton. Returnserr,nwherenis the number of bytes actually read - r:readinto(buf, n)
- Reads at least
nbytes into buf. Handles ensuring thebufis large enough to accommodatenand bumps the contents marker. Returnserr. - r:stream()
- Returns an io.Stream
- r:recvfd():
- Receives a file descriptor and possibly a
d.Iovec.rmust be a Unix socket. For single and forked processes,io.socketpaircan be used to create this socket along with the sending socket (the one that callssendfd). Returnserr,fd,iov.
local levee = require("levee")
local d = require("levee").d
local h = levee.Hub()
local r, w = h.io:socketpair(C.AF_UNIX, C.SOCK_STREAM)
local iov = d.Iovec(1)
local out = h.io:stdout()
out:write("My name is what?\n")
iov:write("Slim Shady\n")
w:sendfd(out.no, iov)
err, fd, iov = r:recvfd()
out = h.io:w(fd)
out:write(iov:string())
io.W
- w:write(buf, [len])
- Writes
buf.bufcan either be achar *or astring.lendefaults to#buf. Returnserr. - w:sendfd(fd, [iov]):
- Sends a file descriptor,
fd, and an optionald.Iovec,iov.wmust be a Unix socket. For single and forked processes,io.socketpaircan be used to create this socket along with the receiving socket (the one that callsrecvfd). Returnserr
io.Stream
A stream is a combination of an io.R and a d.Buffer.
- stream:readin([n])
- Reads additional bytes into this stream's buffer.
nis optional. If supplied this call will block until at leastnbytes are available in the buffer. If that many bytes are already available, it will return immediately. Ifnis not supplied this call will block until one successful read has been made.
Objects: HTTP
HTTP Request
HTTP Response
Misc: TLS Options
Certificate Authority:
ca = BYTES # root certificates from string
ca_path = DIRECTORY # directory searched for root certificates
ca_file = FILE # file containing the root certificates
Certificate:
cert = BYTES # public certificate from string
cert_file = FILE # file containing the public certificate
Key:
key = BYTES # private key from string
key_file = FILE # file containing the private key
Ciphers:
ciphers = "secure" # use the secure ciphers only (default)
ciphers = "compat" # OpenSSL compatibility
ciphers = "legacy" # (not documented)
ciphers = "insecure" # all ciphers available
ciphers = "all" # same as "insecure"
ciphers = STRING # see CIPHERS section of openssl(1)
DHE Params:
dheparams = STRING # (not documented)
ECDHE Curve:
ecdhecurve = STRING # (not documented)
Protocols:
protocols = "TLSv1.0" # only TLSv1.0
protocols = "TLSv1.1" # only TLSv1.1
protocols = "TLSv1.2" # only TLSv1.2
protocols = "ALL" # all supported protocols
protocols = "DEFAULT" # currently TLSv1.2
protocols = LIST # any combination of the above strings
Verfiy Depth:
verify_depth = NUMBER # limit verification depth (?)
Server:
server = {
prefer_ciphers = "server" # prefer client cipher list (less secure)
prefer_ciphers = "client" # prefer server cipher list (more secure, default)
verify_client = true # require client to send certificate
verify_client = "optional" # enable client to send certificate
}
Insecure:
insecure = {
verify_cert = false # disable certificate verification
verify_name = false # disable server name verification for client
verify_time = false # disable validity checking of certificates
}
Objects: DNS
- h.dns:resolve(name, [type], [options])
- Queries DNS server(s) for the domain
namewith recordtype.typedefaults toA. A UDP connection is established to the first name server in/etc/resolv.conf. A failure will cause the next name server in this file to be tried, and so on. A maximum of three servers are tried. The name server'shostandportand the file/etc/resolv.confcan be overriden usingoptions(see below).hostand/orportwill replace all of the entries in/etc/resolv.confor its overriding equivalent, if they are specified. Returnserr,recordswhererecordsis an array of tables with the fields:
-
name: the domain name used in the query -
type: the record type, one of A, NS, CNAME, SOA, PTR, MX, TXT, AAAA SRV, OPT, SSHFP, SPF, AXFR. -
record: the record data, for types A and AAAA this is the IP address -
ttl: the time in seconds that the record may be cached
Options are:
-
port: name server port to connect to (default: 53) -
host: name server host to connect to -
timeout: timeout for reads on this connection -
resconf: path to a file which overrides/etc/resolv.conf
require("levee")._
_ is for utilities
Network
- _.endpoint_in(host, port)
- Returns
ep, an IPV4 Endpoint for the givenhostandport. - _.endpoint_unix(name)
- Returns
ep, a Unix Domain Endpoint for the given pathname. - _.socket(domain, socktype, [protocol])
- Returns
err,no.protocoldefaults to0 - _.connect = function(no, endpoint)
- Connects socket
noto endpointendpoint. Returnserr,no - _.bind = function(no, endpoint)
- Binds socket
noto endpointendpoint. Returnserr,no - _.listen = function(no, endpoint, [backlog])
- Binds and listens socket
noto endpointendpoint. Returnserr,no - _.sendto(no, who, buf, len)
- Sends
buf,lenover file descriptornoto Endpointwho. Returnserr,n, wherenis the number of characters sent on success. - _.recvfrom(no, buf, len)
- Receives from file descriptor
nointobuf,len. Returnserr,who,nwherewhois an Endpoint object of the sender andnis the number of bytes received.
File System
- _.stat(path)
- Returns
err,statinfofor the file pointed to by path wherestatinfois a Stat object.
Stat
- stat:is_reg()
- Returns
trueif this is a regular file. - stat:is_dir()
- Returns
trueif this is a directory.
require("levee").d
d is for data structure thingies
d.Buffer
A Buffer is designed to be a reusable scratch pad of memory. It can grow
dynamically if it's initial sizing is too small, but eventually you usually
want the size of the buffer to reach a steady state. It's the work horse data
structure of the Levee library. It is used, for example, to create streaming
protocol parsers. The parser reads bytes into the Buffer until the next token
in the protocol is reached and the parser can then yield the next portion of
the protocol and then :trim the Buffer to reset the memory allocation for
reuse.
- d.Buffer([bytes])
- allocates and returns a new
buf.bytesis a sizing hint for the initial allocation of memory for thisBuffer - buf:ensure([bytes])
- ensures the
Bufferhas at leastbytesavailable of allocated space, in addition to what's currently in use. - buf:write(buf, [len])
- copies
bufinto the tail of theBuffer.bufcan either be achar *or astring.lendefaults to#buf. Write ensures the buffer is large enough to hold the write and bumps theBuffer's content marker - buf:value([[off], len])
- If
lenis supplied it should be less than the current length of theBuffer.lendefaults to the entireBuffer's contents. The optionaloffoffsets the returnedchar *from the beginning of theBuffer's contents. Returnschar*,len - buf:tail()
- returns
char*,lento the tail of the allocatedBufferthat's not currently in use - buf:bump(len)
- moves the marker for in use bytes by
len - buf:trim([len])
- marks
lenbytes of theBufferas available for reuse. Iflenis not supplied to entireBufferis marked. Returnsn, the number of bytes trimmed
local buf = d.Buffer()
buf:ensure(3)
ffi.copy(buf:tail(), "foo")
buf:bump(3)
ffi.string(buf:value()) -- "foo"
buf:write("bar")
ffi.string(buf:value()) -- "foobar"
ffi.string(buf:value(3)) -- "foo"
ffi.string(buf:value(3, 1)) -- "b"
buf:trim()
ffi.string(buf:value()) -- ""
d.Iovec
- d.Iovec([size])
- todo
require("levee").p
p is for parsing / protocol jobbies
p.json
- p.json.decode(buf, len)
- Decode the JSON compliant string
buf,len.bufcan be a Lua string. Returnserr,data.
local p = require("levee").p
local err, data = p.json.decode([[{"foo": "bar"}]])
data.foo -- "bar"