redbean 1.5
single-file distributable web server

redbean makes it possible to share web applications that run offline as a single-file αcτµαlly pδrταblε εxεcµταblε zip archive which contains your assets. All you need to do is download the redbean.com program below, change the filename to .zip, add your content in a zip editing tool, and then change the extension back to .com.

redbean can serve 1 million+ gzip encoded responses per second on a cheap personal computer. That performance is thanks to zip and gzip using the same compression format, which enables kernelspace copies. Another reason redbean goes fast is that it's a tiny static binary, which makes fork memory paging nearly free.

redbean is also easy to modify to suit your own needs. The program itself is written as a single .c file. It embeds the Lua programming language and SQLite which let you write dynamic pages.

note

this is an old version
click here for the latest redbean

download   [Linux] [Windows] [MacOS] [FreeBSD] [OpenBSD] [NetBSD]

features

installation

curl https://redbean.dev/redbean-latest.com >redbean.com
curl https://redbean.dev/redbean-latest.com.dbg >redbean.com.dbg
chmod +x redbean.com
./redbean.com -v
redbean.com -v              # windows command prompt workaround
bash -c './redbean.com -v'  # zsh/fish workaround (we upstreamed patches!)

usage

Here's basic overview of how you'd go about using redbean alongside InfoZIP which is a free tool that comes included with most UNIX systems.

echo '<b>hello</b>' >hello.html
zip redbean.com hello.html

./redbean.com -vv &

curl http://127.0.0.1:8080/hello.html
<b>hello</b>

echo 'Write("<b>hello</b>")' >hello.lua
zip redbean.com hello.lua

printf 'GET /hello.lua\n\n' | nc 127.0.0.1 8080
<b>hello</b>

One GUI-like feature redbean itself offers is the ability to LaunchBrowser() at startup, which can be called from your your /.init.lua file.

alternative builds

supplementary tools

source build

git clone https://github.com/jart/cosmopolitan && cd cosmopolitan
make -j8 o//tool/net/redbean.com
o//tool/net/redbean.com -vv

details

You can launch redbean in your terminal in the most verbose way possible by doing the following:

./redbean.com -vvmbag         # starts server verbosely
open http://127.0.0.1:8080/   # shows zip listing page
CTRL-C                        # 1x: graceful shutdown
CTRL-C                        # 2x: forceful shutdown

Visit http://redbean.justine.lol/ to see a live demo of redbean-demo-1.0.com.

Assets can be added to the zip archive as follows:

zip redbean.com index.html                  # adds file
zip -r redbean.com mirrored-website         # adds directory
echo comment | zip -c redbean.com foo.html  # adds file w/ comment

By default, anything you add to the archive gets compressed. Sometimes you don't want that to happen. A good example is video files. The web browser will want to send HTTP range requests to seek in the video, in which case redbean requires that the asset be uncompressed.

zip -0 redbean.com video.mp4  # adds file without compression

You can run redbean interactively in your terminal as follows:

redbean.com -vv
CTRL-C                        # 1x: graceful shutdown
CTRL-C                        # 2x: forceful shutdown

The index.lua and index.html names are special since they're used to automatically figure out how to serve directories. Such files can appear in any directory, however the root directory is special. The default action for / is to show a listing page showing the contents of your zip central directory.

The listing page only applies to the root directory. However the default index page applies to subdirectories too. In order for it to work, there needs to be an empty directory entry in the zip. That should already be the default practice of your zip editor.

redbean normalizes the trailing slash for you automatically:

$ printf 'GET /a.example HTTP/1.0\n\n' | nc 127.0.0.1 8080
HTTP/1.0 307 Temporary Redirect
Location: /a.example/

Virtual hosting is accomplished this way too. The Host is simply prepended to the path, and if it doesn't exist, it gets removed.

$ printf 'GET / HTTP/1.1\nHost:a.example\n\n' | nc 127.0.0.1 8080
HTTP/1.1 200 OK
Link: <http://127.0.0.1/a.example/index.html>; rel=\"canonical\"

If you mirror a lot of websites within your redbean then you can actually tell your browser that redbean is your proxy server, in which redbean will act as your private version of the Internet.

wget                     \
  --mirror               \
  --convert-links        \
  --adjust-extension     \
  --page-requisites      \
  --no-parent            \
  --no-if-modified-since \
  http://a.example/index.html
zip -r redbean.com a.example/  # default page for directory
printf 'GET http://a.example HTTP/1.0\n\n' | nc 127.0.0.1 8080
HTTP/1.0 200 OK
Link: <http://127.0.0.1/a.example/index.html>; rel="canonical"

If you use a reverse proxy, then redbean recognizes the following provided that the proxy forwards requests over the local network:

X-Forwarded-For: 203.0.113.42:31337
X-Forwarded-Host: foo.example:80

There's a text/plain statistics page called /statusz that makes it easy to track and monitor the health of your redbean:

printf 'GET /statusz\n\n' | nc 127.0.0.1 8080

redbean will display an error page using the /redbean.png logo by default, embedded as a bas64 data uri. You can override the custom page for various errors by adding files to the zip root.

zip redbean.com 404.html      # custom not found page

flags

-h help
-s increase silence [repeatable]
-v increase verbosity [repeatable]
-d daemonize
-u uniprocess
-z print port
-m log messages
-b log message bodies
-a log resource usage
-g log handler latency
-j enable ssl fetch verify
-k disable ssl fetch verify
-f log worker function calls
-B only use stronger cryptography
-z print port [useful with -p0 for automation]
-H K:V sets http header globally [repeatable]
-t MS timeout ms or keepalive sec if <0 [default 60000]
-M INT tunes max message payload size [default 65536]
-D DIR serve assets from local filesystem directory [repeatable]
-c INT configures static asset cache-control headers
-r /X=/Y redirect /X to /Y [repeatable]
-R /X=/Y rewrite /X to /Y [repeatable]
-K PATH tls private key path [repeatable]
-C PATH tls certificate(s) path [repeatable]
-l ADDR listen addr [default 0.0.0.0; repeatable]
-p PORT listen port [default 8080; repeatable]
-L PATH log file location
-P PATH pid file location
-U INT daemon set user id
-G INT daemon set group id

lua server pages

Any files with the extension .lua will be dynamically served by redbean. Here's the simplest possible example:

Write('<b>Hello World</b>')

The Lua Server Page above should be able to perform at 700,000 responses per second on a Core i9, without any sort of caching. If you want a Lua handler that can do 1,000,000 responses per second, then try adding the following global handler to your /.init.lua file:

function OnHttpRequest()
   Write('<b>Hello World</b>')
end

Here's an example of a more typical workflow for Lua Server Pages using the redbean API:

SetStatus(200)
SetHeader('Content-Type', 'text/plain; charset=utf-8')
Write('<p>Hello ')
Write(EscapeHtml(GetParam('name')))

We didn't need the first two lines in the previous example, because they're implied by redbean automatically if you don't set them. Responses are also buffered until the script finishes executing. That enables redbean to make HTTP as easy as possible. In the future, API capabilities will be expanded to make possible things like websockets.

redbean embeds the Lua standard library. You can use packages such as io to persist and share state across requests and connections, as well as the StoreAsset function, and the lsqlite3 module.

Your Lua interpreter begins its life in the main process at startup in the .init.lua, which is likely where you'll want to perform all your expensive one-time operations like importing modules. Then, as requests roll in, isolated processes are cloned from the blueprint you created.

special paths

/
redbean will generate a zip central directory listing for this page, and this page only, but only if there isn't an /index.lua or /index.html file defined.
/.init.lua
This script is run once in the main process at startup. This lets you modify the state of the Lua interpreter before connection processes are forked off. For example, it's a good idea to do expensive one-time computations here. You can also use this file to call the ProgramFOO() functions below. The init module load happens after redbean's arguments and zip assets have been parsed, but before calling functions like socket() and fork(). Note that this path is a hidden file so that it can't be unintentionally run by the network client.
/.reload.lua
This script is run from the main process when SIGHUP is received. This only applies to redbean when running in daemon mode. Any changes that are made to the Lua interpreter state will be inherited by future forked connection processes. Note that this path is a hidden file so that it can't be unintentionally run by the network client.
/.lua/...
Your Lua modules go in this directory. The way it works is redbean sets Lua's package.path to /zip/.lua/?.lua;/zip/.lua/?/init.lua by default. Cosmopolitan Libc lets system calls like open read from the ZIP structure, if the filename is prefixed with /zip/. So this works like magic.
/redbean.png
If it exists, it'll be used as the / listing page icon, embedded as a base64 URI.

globals

argv: array[str]
Array of command line arguments, excluding those parsed by getopt() in the C code, which stops parsing at the first non-hyphenated arg. In some cases you can use the magic -- argument to delimit C from Lua arguments.

hooks

OnHttpRequest()
If this function is defined in the global scope by your /.init.lua then redbean will call it at the ealiest possible moment to hand over control for all messages (with the exception of OPTIONS *). See functions like Route which asks redbean to do its default thing from the handler.
OnClientConnection(ip:int,port:int,serverip:int,serverport:int) → bool
If this function is defined it'll be called from the main process each time redbean accepts a new client connection. If it returns true then redbean will close the connection without calling fork.
OnProcessCreate(pid:int,ip:int,port:int,serverip:int,serverport:int)
If this function is defined it'll be called from the main process each time redbean forks a connection handler worker process. The ip/port of the remote client is provided, along with the ip/port of the listening interface that accepted the connection. This may be used to create a server activity dashboard, in which case the data provider handler should set SetHeader('Connection','Close'). This won't be called in uniprocess mode.
OnProcessDestroy(pid:int)
If this function is defined it'll be called from the main process each time redbean reaps a child connection process using wait4(). This won't be called in uniprocess mode.
OnServerStart()
If this function is defined it'll be called from the main process right before the main event loop starts.
OnServerStop()
If this function is defined it'll be called from the main process after all the connection processes have been reaped and exit() is ready to be called.
OnWorkerStart()
If this function is defined it'll be called from the child worker process after it's been forked and before messages are handled. This won't be called in uniprocess mode.
OnWorkerStop()
If this function is defined it'll be called from the child worker process once _exit() is ready to be called. This won't be called in uniprocess mode.

functions

Write(data:str)
Appends data to HTTP response payload buffer. This is buffered independently of headers.
SetStatus(code:int[,reason:str])
Starts an HTTP response, specifying the parameters on its first line. reason is optional since redbean can fill in the appropriate text for well-known magic numbers, e.g. 200, 404, etc. This method will reset the response and is therefore mutually exclusive with ServeAsset and other Serve* functions. If a status setting function isn't called, then the default behavior is to send 200 OK.
SetHeader(name:str,value:str)
Appends HTTP header to response header buffer. name is case-insensitive and restricted to non-space ASCII. value is a UTF-8 string that must be encodable as ISO-8859-1. Leading and trailing whitespace is trimmed automatically. Overlong characters are canonicalized. C0 and C1 control codes are forbidden, with the exception of tab. This function automatically calls SetStatus(200, "OK") if a status has not yet been set. As SetStatus and Serve* functions reset the response, SetHeader needs to be called after SetStatus and Serve* functions are called. The header buffer is independent of the payload buffer. Neither is written to the wire until the Lua Server Page has finished executing. This function disallows the setting of certain headers such as and Content-Range which are abstracted by the transport layer. In such cases, consider calling ServeAsset.
SetCookie(name:str,value:str[,options:table])
Appends Set-Cookie HTTP header to the response header buffer. Several Set-Cookie headers can be added to the same response. __Host- and __Secure- prefixes are supported and may set or overwrite some of the options (for example, specifying __Host- prefix sets the Secure option to true, sets the path to "/", and removes the Domain option). The following options can be used (their lowercase equivalents are supported as well):
GetParam(name:str) → value:str
Returns first value associated with name. name is handled in a case-sensitive manner. This function checks Request-URL parameters first. Then it checks application/x-www-form-urlencoded from the message body, if it exists, which is common for HTML forms sending POST requests. If a parameter is supplied matching name that has no value, e.g. foo in ?foo&bar=value, then the returned value will be nil, whereas for ?foo=&bar=value it would be "". To differentiate between no-equal and absent, use the HasParam function. The returned value is decoded from ISO-8859-1 (only in the case of Request-URL) and we assume that percent-encoded characters were supplied by the client as UTF-8 sequences, which are returned exactly as the client supplied them, and may therefore may contain overlong sequences, control codes, NUL characters, and even numbers which have been banned by the IETF. It is the responsibility of the caller to impose further restrictions on validity, if they're desired.
EscapeHtml(str) → str
Escapes HTML entities: The set of entities is &><"' which become &amp;&gt;&lt;&quot;&#39;. This function is charset agnostic and will not canonicalize overlong encodings. It is assumed that a UTF-8 string will be supplied. See escapehtml.c.
LaunchBrowser([path:str])
Launches web browser on local machine with URL to this redbean server. This function may be called from your /.init.lua.
CategorizeIp(ip:uint32) → str
Returns a string describing an IP address. This is currently Class A granular. It can tell you if traffic originated from private networks, ARIN, APNIC, DOD, etc.
DecodeBase64(ascii:str) → binary:str
Turns ASCII into binary, in a permissive way that ignores characters outside the base64 alphabet, such as whitespace. See decodebase64.c.
DecodeLatin1(iso-8859-1:str) → utf-8:str
Turns ISO-8859-1 string into UTF-8.
EncodeBase64(binary:str) → ascii:str
Turns binary into ASCII. This can be used to create HTML data: URIs that do things like embed a PNG file in a web page. See encodebase64.c.
EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str
Turns UTF-8 into ISO-8859-1 string.
EscapeFragment(str) → str
Escapes URL #fragment. The allowed characters are -/?.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. Please note that '& can still break HTML and that '() can still break CSS URLs. This function is charset agnostic and will not canonicalize overlong encodings. It is assumed that a UTF-8 string will be supplied. See kescapefragment.c.
EscapeHost(str) → str
Escapes URL host. See kescapeauthority.c
EscapeLiteral(str) → str
Escapes JavaScript or JSON string literal content. The caller is responsible for adding the surrounding quotation marks. This implementation \uxxxx sequences for all non-ASCII sequences. HTML entities are also encoded, so the output doesn't need EscapeHtml. This function assumes UTF-8 input. Overlong encodings are canonicalized. Invalid input sequences are assumed to be ISO-8859-1. The output is UTF-16 since that's what JavaScript uses. For example, some individual codepoints such as emoji characters will encode as multiple \uxxxx sequences. Ints that are impossible to encode as UTF-16 are substituted with the \xFFFD replacement character. See escapejsstringliteral.c.
EscapeParam(str) → str
Escapes URL parameter name or value. The allowed characters are -.*_0-9A-Za-z and everything else gets %XX encoded. This function is charset agnostic and will not canonicalize overlong encodings. It is assumed that a UTF-8 string will be supplied. See kescapeparam.c.
EscapePass(str) → str
Escapes URL password. See kescapeauthority.c.
EscapePath(str) → str
Escapes URL path. This is the same as EscapeSegment except slash is allowed. The allowed characters are -.~_@:!$&'()*+,;=0-9A-Za-z/ and everything else gets %XX encoded. Please note that '& can still break HTML, so the output may need EscapeHtml too. Also note that '() can still break CSS URLs. This function is charset agnostic and will not canonicalize overlong encodings. It is assumed that a UTF-8 string will be supplied. See kescapepath.c.
EscapeSegment(str) → str
Escapes URL path segment. This is the same as EscapePath except slash isn't allowed. The allowed characters are -.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. Please note that '& can still break HTML, so the output may need EscapeHtml too. Also note that '() can still break CSS URLs. This function is charset agnostic and will not canonicalize overlong encodings. It is assumed that a UTF-8 string will be supplied. See kescapesegment.c.
EscapeUser(str) → str
Escapes URL username. See kescapeauthority.c.
Fetch(url:str[,body:str|{method=value:str,body=value:str,...}]) → status:int,{header:str=value:str,...},body:str
Sends an HTTP/HTTPS request to the specified URL. If only the URL is provided, then a GET request is sent. If both URL and body parameters are specified, then a POST request is sent. If any other method needs to be specified (for example, PUT or DELETE), then passing a table as the second value allows setting method and body values as well other options: When the redirect is being followed, the same method and body values are being sent in all cases except when 303 status is returned. In that case the method is set to GET and the body is removed before the redirect is followed. Note that if these (method/body) values are provided as table fields, they will be modified in place.
FormatHttpDateTime(seconds:int) → rfc1123:str
Converts UNIX timestamp to an RFC1123 string that looks like this: Mon, 29 Mar 2021 15:37:13 GMT. See formathttpdatetime.c.
FormatIp(uint32) → str
Turns integer like 0x01020304 into a string like 1.2.3.4. See also ParseIp for the inverse operation.
GetAssetMode(path:str) → int
Returns UNIX-style octal mode for ZIP asset (or local file if the -D flag is used)
GetAssetSize(path:str) → int
Returns byte size of uncompressed contents of ZIP asset (or local file if the -D flag is used)
GetRemoteAddr() → ip:uint32,port:uint16
Returns client ip4 address and port, e.g. 0x01020304,31337 would represent 1.2.3.4:31337. This is the same as GetClientAddr except it will use the ip:port from the X-Forwarded-For header, only if IsPrivateIp or IsPrivateIp returns true.
GetClientAddr() → ip:uint32,port:uint16
Returns client socket ip4 address and port, e.g. 0x01020304,31337 would represent 1.2.3.4:31337. Please consider using GetRemoteAddr instead, since the latter takes into consideration reverse proxy scenarios.
GetServerAddr() → ip:uint32,port:uint16
Returns address to which listening server socket is bound, e.g. 0x01020304,8080 would represent 1.2.3.4:8080. If -p 0 was supplied as the listening port, then the port in this string will be whatever number the operating system assigned.
GetDate() → seconds:int
Returns date associated with request that's used to generate the Date header, which is now, give or take a second. The returned value is a UNIX timestamp.
GetHeader(name:str) → value:str
Returns HTTP header. name is case-insensitive. The header value is returned as a canonical UTF-8 string, with leading and trailing whitespace trimmed, which was decoded from ISO-8859-1, which is guaranteed to not have C0/C1 control sequences, with the exception of the tab character. Leading and trailing whitespace is automatically removed. In the event that the client suplies raw UTF-8 in the HTTP message headers, the original UTF-8 sequence can be losslessly restored by counter-intuitively recoding the returned string back to Latin1. If the requested header is defined by the RFCs as storing comma-separated values (e.g. Allow, Accept-Encoding) and the field name occurs multiple times in the message, then this function will fold those multiple entries into a single string.
GetHeaders() → {name:str=value:str,...}
Returns HTTP headers as dictionary mapping header key strings to their UTF-8 decoded values. The ordering of headers from the request message is not preserved. Whether or not the same key can repeat depends on whether or not it's a standard header, and if so, if it's one of the ones that the RFCs define as repeatable. See khttprepeatable.c. Those headers will not be folded. Standard headers which aren't on that list, will be overwritten with the last-occurring one during parsing. Extended headers are always passed through exactly as they're received. Please consider using GetHeader API if possible since it does a better job abstracting these issues.
GetLogLevel() → int
Returns logger verbosity level. Likely return values are kLogDebug > kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal.
GetHost() → str
Returns host associated with request. This will be the Host header, if it's supplied. Otherwise it's the bind address.
GetHostOs() → str
Returns string that describes the host OS.
GetMonospaceWidth(str|char) → int
Returns monospace display width of string. This is useful for fixed-width formatting. For example, CJK characters typically take up two cells. This function takes into consideration combining characters, which are discounted, as well as control codes and ANSI escape sequences.
GetMethod() → str
Returns HTTP method. Normally this will be GET, HEAD, or POST in which case redbean normalizes this value to its uppercase form. Anything else that the RFC classifies as a "token" string is accepted too, which might contain characters like &".
GetParams() → {{name:str[,value:str]},...}
Returns name=value parameters from Request-URL and application/x-www-form-urlencoded message body in the order they were received. This may contain duplicates. The inner array will have either one or two items, depending on whether or not the equals sign was used.
GetPath() → str
Returns the Request-URL path. This is guaranteed to begin with "/". It is further guaranteed that no "//" or "/." exists in the path. The returned value is returned as a UTF-8 string which was decoded from ISO-8859-1. We assume that percent-encoded characters were supplied by the client as UTF-8 sequences, which are returned exactly as the client supplied them, and may therefore may contain overlong sequences, control codes, NUL characters, and even numbers which have been banned by the IETF. redbean takes those things into consideration when performing path safety checks. It is the responsibility of the caller to impose further restrictions on validity, if they're desired.
GetEffectivePath() → str
Returns path as it was resolved by the routing algorithms, which might contain the virtual host prepended if used.
GetScheme() → str
Returns scheme from Request-URL, if any.
GetPayload() → str
Returns the request message payload, or empty string if there isn't one.
GetStatus() → int
Returns current status (as set by an earlier SetStatus call) or nil if the status hasn't been set yet.
GetTime() → seconds:number
Returns current time as a UNIX timestamp with 0.0001s precision.
GetUrl() → str
Returns the effective Request-URL as an ASCII string, where illegal characters or UTF-8 is guaranteed to be percent encoded, and has been normalized to include either the Host or X-Forwarded-Host headers, if they exist, and possibly a scheme too if redbean is being used as an HTTP proxy server. In the future this API might change to return an object instead.
GetHttpVersion() → int
Returns the request HTTP protocol version, which can be 9 for HTTP/0.9, 10 for HTTP/1.0, or 11 for HTTP/1.1.
also available under the deprecated name GetVersion
GetRandomBytes([length:int]) → str
Returns string with the specified number of random bytes (1..256). If no length is specified, then a string of length 16 is returned.
GetRedbeanVersion() → int
Returns the Redbean version in the format 0xMMmmpp, with major (MM), minor (mm), and patch (pp) versions encoded. The version value 1.4 would be represented as 0x010400.
GetZipPaths([prefix:str]) → {path:str,...}
Returns paths of all assets in the zip central directory, prefixed by a slash. If prefix parameter is provided, then only paths that start with the prefix (case sensitive) are returned.
HasParam(name:str) → bool
Returns true if parameter with name was supplied in either the Request-URL or an application/x-www-form-urlencoded message body.
HidePath(prefix:str)
Programs redbean / listing page to not display any paths beginning with prefix. This function should only be called from /.init.lua.
IsPublicIp(uint32) → bool
Returns true if IP address is not a private network (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and is not localhost (127.0.0.0/8). Note: we intentionally regard TEST-NET IPs as public.
IsPrivateIp(uint32) → bool
Returns true if IP address is part of a private network (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16).
IsLoopbackClient() → bool
Returns true if the client IP address (returned by GetRemoteAddr) is part of the localhost network (127.0.0.0/8).
IsLoopbackIp(uint32) → bool
Returns true if IP address is part of the localhost network (127.0.0.0/8).
IsCompressed(path:str) → bool
Returns true if ZIP artifact at path is stored on disk using DEFLATE compression.
IndentLines(str[,int]) → str
Adds spaces to beginnings of multiline string. If the int parameter is not supplied then 1 space will be added.
LoadAsset(path:str) → str
Returns contents of file as string. The asset may be sourced from either the zip (decompressed) or the local filesystem if the -D flag was used. If slurping large file into memory is a concern, then consider using ServeAsset which can serve directly off disk.
StoreAsset(path:str,data:str,mode:int)
Stores asset to executable's ZIP central directory. This currently happens in an append-only fashion and is still largely in the proof-of-concept stages. Currently only supported on Linux, XNU, and FreeBSD.
Log(level:int,message:str)
Emits message string to log, if level is less than or equal to GetLogLevel. If redbean is running in interactive mode, then this will log to the console. If redbean is running as a daemon or the -L LOGFILE flag is passed, then this will log to the file. Reasonable values for level are kLogDebug > kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. The logger emits timestamps in the local timezone with microsecond precision. If log entries are emitted more frequently than once per second, then the log entry will display a delta timestamp, showing how much time has elapsed since the previous log entry. This behavior is useful for quickly measuring how long various portions of your code take to execute.
ParseHttpDateTime(rfc1123:str) → seconds:int
Converts RFC1123 string that looks like this: Mon, 29 Mar 2021 15:37:13 GMT to a UNIX timestamp. See parsehttpdatetime.c.
ParseUrl(str) → URL
Parses URL, returning object having the following fields: scheme, user, pass, host, port, path, params, fragment. This parser is charset agnostic. Percent encoded bytes are decoded for all fields. Returned values might contain things like NUL characters, spaces, control codes, and non-canonical encodings. Absent can be discerned from empty by checking if the pointer is set. There's no failure condition for this routine. This is a permissive parser. This doesn't normalize path segments like `.` or `..` so use IsAcceptablePath() to check for those. No restrictions are imposed beyond that which is strictly necessary for parsing. All the data that is provided will be consumed to the one of the fields. Strict conformance is enforced on some fields more than others, like scheme, since it's the most non-deterministically defined field of them all. Please note this is a URL parser, not a URI parser. Which means we support everything the URI spec says we should do except for the things we won't do, like tokenizing path segments into an array and then nesting another array beneath each of those for storing semicolon parameters. So this parser won't make SIP easy. What it can do is parse HTTP URLs and most URIs like data:opaque, better in fact than most things which claim to be URI parsers.
EncodeUrl(URL) → str
This function is the inverse of ParseUrl. The output will always be correctly formatted. The exception is if illegal characters are supplied in the scheme field, since there's no way of escaping those. Opaque parts are escaped as though they were paths, since many URI parsers won't understand things like an unescaped question mark in path.
ParseIp(str) → int
Converts IPv4 address string to integer, e.g. "1.2.3.4" → 0x01020304, or returns -1 for invalid inputs. See also FormatIp for the inverse operation.
GetAssetComment(path:str) → str
Returns comment text associated with asset in the ZIP central directory. Also available as GetComment (deprecated).
GetBody() → str
Returns the request message body if present or an empty string.
also available under the deprecated name GetPayload
GetCookie(name:str) → str
Returns cookie value.
Md5(str) → str
Computes MD5 checksum, returning 16 bytes of binary.
Sha1(str) → str
Computes SHA1 checksum, returning 20 bytes of binary.
Sha224(str) → str
Computes SHA224 checksum, returning 28 bytes of binary.
Sha256(str) → str
Computes SHA256 checksum, returning 32 bytes of binary.
Sha384(str) → str
Computes SHA384 checksum, returning 48 bytes of binary.
Sha512(str) → str
Computes SHA512 checksum, returning 64 bytes of binary.
GetCryptoHash(name:str,payload:str[,key:str]) → str
Returns value of the specified cryptographic hash function. If the key is provided, then HMAC value of the same function is returned. The name can be one of the following strings: MD5, SHA1, SHA224, SHA256, SHA384, SHA512, and BLAKE2B256.
IsDaemon() → bool
Returns true if -d flag was passed to redbean.
ProgramAddr(str)
Configures the address on which to listen. Can be used multiple times to set more than one address.
ProgramUid(int)
Same as the -U flag if called from .init.lua for setuid()
ProgramGid(int)
Same as the -G flag if called from .init.lua for setgid()
ProgramPidPath(str)
Same as the -P flag if called from .init.lua for setting the pid file path on the local file system. It's useful for reloading daemonized redbean using kill -HUP $(cat /var/run/redbean.pid) or terminating redbean with kill $(cat /var/run/redbean.pid) which will gracefully terminate all clients. Sending the TERM signal twice will cause a forceful shutdown, which might make someone with a slow internet connection who's downloading big files unhappy.
ProgramDirectory(str)
Same as the -D flag if called from .init.lua for overlaying local file system directories. This may be called multiple times. The first directory programmed is preferred. These currently do not show up in the index page listing.
ProgramLogMessages(bool)
Same as the -m flag if called from .init.lua for logging message headers only.
ProgramHeader(name:str,value:str)
Appends HTTP header to the header buffer for all responses (whereas SetHeader only appends a header to the current response buffer). name is case-insensitive and restricted to non-space ASCII. value is a UTF-8 string that must be encodable as ISO-8859-1. Leading and trailing whitespace is trimmed automatically. Overlong characters are canonicalized. C0 and C1 control codes are forbidden, with the exception of tab. The header buffer is independent of the payload buffer. This function disallows the setting of certain headers such as Content-Range and Date, which are abstracted by the transport layer.
ProgramSslTicketLifetime(seconds:int)
Defaults to 86400 (24 hours). This may be set to ≤0 to disable SSL tickets. It's a good idea to use these since it increases handshake performance 10x and eliminates a network round trip.
EvadeDragnetSurveillance(bool)
If this option is programmed then redbean will not transmit a Server Name Indicator (SNI) when performing Fetch() requests.
ProgramBrand(str)
Changes HTTP Server header, as well as the <h1> title on the / listing page. The brand string needs to be a UTF-8 value that's encodable as ISO-8859-1. If the brand is changed to something other than redbean, then the promotional links will be removed from the listing page too. This function should only be called from /.init.lua.
ProgramCache(seconds:int)
Configures Cache-Control and Expires header generation for static asset serving. A negative value will disable the headers. Zero means don't cache. Greater than zero asks public proxies and browsers to cache for a given number of seconds. This should only be called from /.init.lua.
ProgramPort(uint16)
Hard-codes the port number on which to listen, which can be any number in the range 1..65535, or alternatively 0 to ask the operating system to choose a port, which may be revealed later on by GetServerAddr or the -z flag to stdout.
ProgramRedirect(code:int,src:str,location:str)
Configures fallback routing for paths which would otherwise return 404 Not Found. If code is 0 then the path is rewritten internally as an accelerated redirect. If code is 301, 302, 307, or 308 then a redirect response will be sent to the client. This should only be called from /.init.lua.
ProgramCertificate(pem:str)
This function is the same as the -C flag if called from .init.lua, e.g. ProgramCertificate(LoadAsset("/.sign.crt")) for zip loading or ProgramCertificate(Slurp("/etc/letsencrypt.lol/fullchain.pem")) for local file system only.
ProgramPrivateKey(pem:str)
This function is the same as the -K flag if called from .init.lua, e.g. ProgramPrivateKey(LoadAsset("/.sign.key")) for zip loading or ProgramPrivateKey(Slurp("/etc/letsencrypt/privkey.pem")) for local file system only.
ProgramSslPresharedKey(key:str,identity:str)
This function can be used to enable the PSK ciphersuites which simplify SSL and enhance its performance in controlled environments. key may contain 1..32 bytes of random binary data and identity is usually a short plaintext string. The first time this function is called, the preshared key will be added to both the client and the server SSL configs. If it's called multiple times, then the remaining keys will be added to the server, which is useful if you want to assign separate keys to each client, each of which needs a separate identity too. If this function is called multiple times with the same identity string, then the latter call will overwrite the prior. If a preshared key is supplied and no certificates or key-signing-keys are programmed, then redbean won't bother auto-generating any serving certificates and will instead use only PSK ciphersuites.
ProgramSslCiphersuite(name:str)
This function may be called multiple times to specify the subset of available ciphersuites you want to use in both the HTTPS server and the Fetch() client. The default list, ordered by preference, is as follows:
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-ECDSA-CHACHA20-POLY1305-SHA256
ECDHE-PSK-AES256-GCM-SHA384
ECDHE-PSK-AES128-GCM-SHA256
ECDHE-PSK-CHACHA20-POLY1305-SHA256
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-CHACHA20-POLY1305-SHA256
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-CHACHA20-POLY1305-SHA256
ECDHE-ECDSA-AES128-CBC-SHA256
ECDHE-RSA-AES256-CBC-SHA384
ECDHE-RSA-AES128-CBC-SHA256
DHE-RSA-AES256-CBC-SHA256
DHE-RSA-AES128-CBC-SHA256
ECDHE-PSK-AES256-CBC-SHA384
ECDHE-PSK-AES128-CBC-SHA256
ECDHE-ECDSA-AES256-CBC-SHA
ECDHE-ECDSA-AES128-CBC-SHA
ECDHE-RSA-AES256-CBC-SHA
ECDHE-RSA-AES128-CBC-SHA
DHE-RSA-AES256-CBC-SHA
DHE-RSA-AES128-CBC-SHA
ECDHE-PSK-AES256-CBC-SHA
ECDHE-PSK-AES128-CBC-SHA
RSA-AES256-GCM-SHA384
RSA-AES128-GCM-SHA256
RSA-AES256-CBC-SHA256
RSA-AES128-CBC-SHA256
RSA-AES256-CBC-SHA
RSA-AES128-CBC-SHA
PSK-AES256-GCM-SHA384
PSK-AES128-GCM-SHA256
PSK-CHACHA20-POLY1305-SHA256
PSK-AES256-CBC-SHA384
PSK-AES128-CBC-SHA256
PSK-AES256-CBC-SHA
PSK-AES128-CBC-SHA
ECDHE-RSA-3DES-EDE-CBC-SHA
DHE-RSA-3DES-EDE-CBC-SHA
ECDHE-PSK-3DES-EDE-CBC-SHA
RSA-3DES-EDE-CBC-SHA
PSK-3DES-EDE-CBC-SHA
The names above are canonical to redbean. They were programmatically simplified from the official IANA names. This function will accept the IANA names too. In most cases it will accept the OpenSSL and GnuTLS naming convention as well.
Route([host:str,[path:str]])
Instructs redbean to follow the normal HTTP serving path. This function is useful when writing an OnHttpRequest handler, since that overrides the serving path entirely. So if the handler decides it doesn't want to do anything, it can simply call this function, to hand over control back to the redbean core. By default, the host and path arguments are supplied from the resolved GetUrl value. This handler always resolves, since it will generate a 404 Not Found response if redbean couldn't find an appropriate endpoint.
Sleep(seconds:number)
Sleeps the specified number of seconds (can be fractional). The smallest interval is a microsecond.
RouteHost([host:str,[path:str]]) → bool
This is the same as Route, except it only implements the subset of request routing needed for serving virtual-hosted assets, where redbean tries to prefix the path with the hostname when looking up a file. This function returns true if the request was resolved. If it was resolved, then your OnHttpRequest request handler can still set additional headers.
RoutePath([path:str]) → bool
This is the same as Route, except it only implements the subset of request routing needed for serving assets. This function returns true if the request was resolved. If it was resolved, then your OnHttpRequest request handler can still set additional headers.
ServeAsset(path:str)
Instructs redbean to serve static asset at path. This function causes what would normally happen outside a dynamic handler to happen. The asset can be sourced from either the zip or local filesystem if -D is used. This function is mutually exclusive with SetStatus and ServeError.
ServeError(code:int[,reason:str])
Instructs redbean to serve a boilerplate error page. This takes care of logging the error, setting the reason phrase, and adding a payload. This function is mutually exclusive with SetStatus and ServeAsset.
SetLogLevel(level:int)
Sets logger verbosity. Reasonable values for level are kLogDebug > kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal.
VisualizeControlCodes(str) → str
Replaces C0 control codes with their UNICODE pictures representation. This function also canonicalizes overlong encodings. C1 control codes are replaced with a JavaScript-like escape sequence.
Underlong(str) → str
Canonicalizes overlong encodings.
Bsf(x:int) → int
Returns position of first bit set. Passing 0 will raise an error. Same as the Intel x86 instruction BSF.
also available under the deprecated name bsf
Bsr(x:int) → int
Returns binary logarithm of 𝑥. Passing 0 will raise an error. Same as the Intel x86 instruction BSR.
also available under the deprecated name bsr
Crc32(initial:int,data:str) → int
Computes Phil Katz CRC-32 used by zip/zlib/gzip/etc.
also available under the deprecated name crc32
Crc32c(initial:int,data:str) → int
Computes 32-bit Castagnoli Cyclic Redundancy Check.
also available under the deprecated name crc32c
Popcnt(x:int) → int
Returns number of bits set in integer.
also available under the deprecated name popcnt

lsqlite3 module

Please refer to the LuaSQLite3 Documentation.

redbean supports a subset of what's defined in the upstream LuaSQLite3 project. Most of the unsupported APIs relate to pointers and database notification hooks.

redbean also currently disables SQLite features which don't make sense for production serving, such as ALTER, VACUUM, ANALYZE, etc. For that reason we provide an APE build of the SQLite shell which you can use to administrate your redbean database. See the sqlite3.com download above.

re module

This module exposes an API for POSIX regular expressions which enable you to validate input, search for substrings, extract pieces of strings, etc. Here's a usage example:

# Example IPv4 Address Regular Expression (see also ParseIP)
p = re.compile([[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]])
m,a,b,c,d = p:search(𝑠)
if m then
  print("ok", tonumber(a), tonumber(b), tonumber(c), tonumber(d))
else
  print("not ok")
end
re.search(regex:str,text:str[,flags:int]) → [match[,group1,...]]
Shortcut for re.compile plus regex_t*:search.
re.compile(regex:str[,flags:int]) → regex_t*
Compiles regular expression, using the POSIX extended syntax. This has an O(2𝑛) cost, so it's a good idea to do this from your /.init.lua file. Flags may contain re.BASIC, re.ICASE, re.NOSUB, and/or re.NEWLINE. See also regcomp() from libc.
Executes regular expression. This has an O(𝑛) cost. This returns nothing (nil) if the pattern doesn't match anything. Otherwise it pushes the matched substring and any parenthesis-captured values too. Flags may contain re.NOTBOL or re.NOTEOL to indicate whether or not text should be considered at the start and/or end of a line.
re.BASIC
Use this flag if you prefer the default POSIX regex syntax. We use extended regex notation by default. For example, an extended regular expression for matching an IP address might look like ([0-9]*)\.([0-9]*)\.([0-9]*)\.([0-9]*) whereas with basic syntax it would look like \([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\). This flag may only be used with re.compile and re.search.
re.ICASE
Use this flag to make your pattern case ASCII case-insensitive. This means [a-z] will mean the same thing as [A-Za-z]. This flag may only be used with re.compile and re.search.
re.NEWLINE
Use this flag to change the handling of NEWLINE (\x0a) characters. When this flag is set, (1) a NEWLINE shall not be matched by a "." or any form of a non-matching list, (2) a "^" shall match the zero-length string immediately after a NEWLINE (regardless of re.NOTBOL), and (3) a "$" shall match the zero-length string immediately before a NEWLINE (regardless of re.NOTEOL).
re.NOSUB
Causes re.search to only report success and failure. This is reported via the API by returning empty string for success. This flag may only be used with re.compile and re.search.
re.NOTBOL
The first character of the string pointed to by string is not the beginning of the line. This flag may only be used with re.search and regex_t*:search.
re.NOTEOL
The last character of the string pointed to by string is not the end of the line. This flag may only be used with re.search and regex_t*:search.

constants

kLogDebug
Integer for debug logging level. See Log.
kLogVerbose
Integer for verbose logging level, which is less than kLogDebug. See Log.
kLogInfo
Integer for info logging level, which is less than kLogVerbose. See Log.
kLogWarn
Integer for warn logging level, which is less than kLogVerbose. See Log.
kLogError
Integer for error logging level, which is less than kLogWarn. See Log.
kLogFatal
Integer for fatal logging level, which is less than kLogError. See Log. Logging anything at this level will result in a backtrace and process exit.

operations

You can have redbean run as a daemon by doing the following:

redbean.com -vv -d -L redbean.log -P redbean.pid
kill -TERM $(cat redbean.pid) # 1x: graceful shutdown
kill -TERM $(cat redbean.pid) # 2x: forceful shutdown

It's possible to modify global interpreter state later on in the server's lifecycle. When running in daemon mode, using kill -HUP $(pidof redbean.com) will instruct redbean to run the code in .reload.lua from the main process, will will be lazily propagated to client connections.

You can modify the zip while redbean is running. The zip command by default will do this by replacing the inode. redbean will detect the changed inode within a second and broadcast SIGUSR1 to the process group so the new assets get indexed as soon as possible. It's also possible to modify the executable assets in place, while the executable is running. If you do that, then you need to be careful to not disturb the local file and central directory. What you would do instead is append changed or new files. Then append a new central directory, along with a new end of central directory record. Finally, memset(0) the old end of central directory record. redbean will detect it's gone and reindex. You can even modify local files in place too. The way you would do that is by clearing the PK♥♦ magic marker while the file memory is being mutated, and then putting it back. Any requests that arrive during the modification will result in a 503 Service Unavailable so your load balancer can failover.

redbean will grow to whatever number of processes your system limits and tcp stack configuration allow. Once functions like fork() and accept() start to fail, redbean will enter "meltdown mode" where it interrupts worker processes to immediately close idling and lagging connections. redbean may need to drop requests by sending 503 Service Unavailable until congestion subsides. So be sure your load balancer is configured to immediately failover to another instance in such cases.

There's a 64kb limit on request message size, where the header portion is further limited to 32kb. We do that to guarantee processes stay tiny. You can tune it using the -M flag. redbean spawns a process for each connection. redbean needs about 200kb of RAM per worker on average. Clients are encouraged to pipeline HTTP requests within the same connection.

redbean rejects requests for hidden files, i.e. any path containing the substring /. and requests with denormalized paths, e.g. /../../etc/passwd are also categorically rejected. Furthermore, redbean won't service requests that come in more than 32 fragments. Those few restrictions aside, redbean generally aims to follow Postel's maxim in the sense that it's liberal in what it accepts but conservative in what it sends.

If you want Rust-like promises then redbean can be compiled with ASAN memory safety. Cosmopolitan Libc has the only open source implementation of the Address Sanitizer runtime that's intended for production use. It causes redbean worker processes to crash and log a report when bugs like buffer overruns, use-after-free, stack smashing, etc. occur. This has a marginal impact on performance and can be useful in environments where interruptions in service are more desirable than risking the system being compromised. Please be sure to report any bugs you might find. It also helps to have the .com.dbg file in the same directory, so backtraces will show the function names.

redbean should not be productionized on Windows and MacOS. These systems are designed to be used by a single person. Both systems have unreliable file locking which is important for SQLite. On Windows redbean currently runs in uniprocess mode on Windows.

security

redbean doesn't secure your computer. It does however provide tools we hope will help you do it yourself. For further details on why things need to be this way, please see the disclaimer in the ISC license.

Some computing environments rely on physical security and redbean is a good fit for that. For example, if you need to run a web app on an air-gapped computer running an old version of some other operating system that can't be upgraded, then you load your redbean off a thumb drive provided that the system was installed after the year 2007 or more specifically runs x86_64 with Linux 2.6.18+ or Windows Vista+.

Some environments require that security be provided using existing infrastructure like SSL frontends. In that case, the "redbean-unsecure" download link might be the right choice for you, since it's a special build of redbean that leaves out the security code. That way, you can bolt the security on separately using a tool like stunnel.

redbean also provides integrated SSL support based on MbedTLS. It's configured to offer 128 bits of security with modern clients, but will fall back to at minimum 112 bits of security depending on the preferences of the client. Both are secure based on public knowledge until 2030 according to NIST. If you'd rather restrict yourself to just 150+ bits of security but with the tradeoff of dropping support for old Internet Explorer and making embedded clients less happy, then pass the -B flag, which'll restrict redbean to a very short list of protocols, algorithms, and parameters that the NSA, NIST, and IANA all agree upon.

redbean's SSL implementation is tuned for performance. It uses hardware algorithms when available such as AES-NI, SHA-NI, and RDRAND. redbean does not use costly hardnening measures specific only to legacy clients like Internet Explorer if they increase denial of service risk for the server as a whole.

redbean is tuned for ease of use. Your redbean uses a protocol polyglot for serving HTTP and HTTPS on the same port numbers. Both the TLS hello and the SSLv2 hello are accepted, even though only TLS is supported. For example, both of these are valid:

http://127.0.0.1:8080/
https://127.0.0.1:8080/

By default, your redbean will automatically generate ephemeral self-signed ECDSA and RSA serving certificates. This causes browser warnings. The simplest option for making the warning go away is to give redbean a key signing key (KSK).

openssl req -x509 -newkey rsa:2048 \
  -keyout .ca.key -out .ca.crt -days 6570 -nodes \
  -subj '/C=US/ST=CA/O=Jane Doe/CN=My Root CA 1' \
  -addext 'keyUsage = critical,cRLSign,keyCertSign'
sudo ./redbean.com -C ca.crt -K .ca.key -p 80 -p 443

Your SSL root can then be installed on client machines as follows:

# linux
sudo cp ca.crt /usr/local/share/ca-certificates
sudo update-ca-certificates

# macos
sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain ca.crt

# windows
certutil -addstore -f "ROOT" ca.crt

# notes
# firefox is special you have to use its settings

If your goal is to make SSL deploys easy, then it's possible to put the KSK inside the redbean.com file using the InfoZIP program. Be sure the key is a hidden file. It can be loaded using your .init.lua script with the LoadAsset, ProgramCertificate, and ProgramPrivateKey APIs. Please note, this is just an example of what you could do; we don't claim it's what you should do.

For a public-facing online service, the simplest way to use SSL is with Let's Encrypt. Let's say you're migrating from NGINX. In that case you'll likely want something like the following:

# commands subject to public monitoring
certbot certonly --nginx --key-type ecdsa \
  --cert-name redbean-ecdsa -d redbean.dev -d www.redbean.dev
certbot certonly --nginx --key-type rsa \
  --cert-name redbean-rsa -d redbean.dev -d www.redbean.dev

You can then program /var/www/html/.init.lua as such:

ProgramPrivateKey(Slurp('/etc/letsencrypt/live/redbean-ecdsa/privkey.pem'))
ProgramCertificate(Slurp('/etc/letsencrypt/live/redbean-ecdsa/fullchain.pem'))
ProgramPrivateKey(Slurp('/etc/letsencrypt/live/redbean-rsa/privkey.pem'))
ProgramCertificate(Slurp('/etc/letsencrypt/live/redbean-rsa/fullchain.pem'))
if IsDaemon() then
   ProgramUid(33)  # see `vipw` to get appropriate number
   ProgramGid(33)  # see `vigr` to get appropriate number
   ProgramPort(80)
   ProgramPort(443)
   ProgramLogPath('/var/log/redbean.log')
   ProgramPidPath('/var/run/redbean.pid')
end
function OnHttpRequest()
   path = GetPath()
   if path == '/favicon.ico' or
      path == '/site.webmanifest' or
      path == '/favicon-16x16.png' or
      path == '/favicon-32x32.png' or
      path == '/apple-touch-icon' then
      SetLogLevel(kLogWarn)
   end
   Route()
   SetHeader('Content-Language', 'en-US')
end

You'd then run redbean as follows:

redbean.com -dD /var/www/html

You can load as many public and private keys as you want. They can be specified as pem, der, concatenated ascii, bundles, or chains. If you don't specify specific chains then redbean will automatically infer it based on SUBJECT → ISSUER relationships. Your redbean won't serve the self-signed root certificate at the end of the chain where self-signed is defined as SUBJECT == ISSUER. Otherwise you can control when chains terminate by setting the max length constraint to zero.

Your redbean supports SSL virtual hosting. 99.76% of TLS clients send a Server Name Indicator (SNI), which is matched against DNS or IPs in Subject Alternative Names (SAN) or the Common Name (CN) of subject if SAN isn't used. This means you don't need to reveal your whole domain portfolio to each client just to have ssl. You can just use different certificates for each domain if you choose to do so. If redbean can't find an appropriate match, then the first certificate will be chosen.

SSL layer client verification is unusual, but some options are:

  1. Pass the -j to enable verification of HTTPS clients. Clients are verified based on the SSL roots you've provided. Those can be installed via the Lua API or placed in the ZIP executable folder usr/share/ssl/root.
  2. You can use preshared keys via the Lua API. Please note that PSK isn't mutually exclusive with normal PKI. Please read the relevant API documentation.

SSL verbosity is controlled as follows for troubleshooting:

-V       log ssl errors
-VV      log ssl state changes too
-VVV     log ssl informational messages too
-VVVV    log ssl verbose details too

That's in addition to existing flags like -vvvm.

limitations

No APIs are currently provided for use-cases like long-polling and websockets. That should change in the future.

benchmark

# Note: Benchmarked on an Intel® Core™ i9-9900 CPU
# Note: Use redbean-demo.com -s
$ wrk -H 'Accept-Encoding: gzip' -t 12 -c 120 \
  http://127.0.0.1:8080/tool/net/demo/index.html
Running 10s test @ http://127.0.0.1:8080/tool/net/demo/index.html
  12 threads and 120 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    18.27ms  131.81ms   1.71s    97.60%
    Req/Sec    85.17k    10.73k  144.05k    82.75%
  10221627 requests in 10.10s, 7.53GB read
  Socket errors: connect 0, read 0, write 0, timeout 13
Requests/sec: 1012088.67
Transfer/sec:    763.48MB

[United States of Lemuria - two dollar bill - all debts public and primate]

Funding for redbean was crowdsourced from Justine Tunney's GitHub sponsors and Patreon subscribers. Your support is what makes projects like redbean possible. Thank you.

see also

old versions