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 tool like
InfoZIP, and 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 which lets you write dynamic pages.
curl https://justine.lol/redbean/redbean-latest.com >redbean.com curl https://justine.lol/redbean/redbean-latest.com.dbg >redbean.com.dbg chmod +x redbean.com ./redbean.com -v bash -c './redbean.com -v' # zsh/fish workaround (we upstreamed patches!)
echo 'Write("<b>hello</b>")' >hello.lua echo '<b>hello</b>' >hello.html zip redbean.com hello.lua zip redbean.com hello.html ./redbean.com -vv curl -v http://127.0.0.1:8080/index.html
git clone https://github.com/jart/cosmopolitan && cd cosmopolitan make -j8 o//tool/net/redbean.com o//tool/net/redbean.com -vv
Assets can be listed by running the following command:
unzip -vl redbean.com # lists files
Which is also implemented as the default index page:
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
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
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.
|-b||log message bodies|
|-z||print port [useful with |
|-D DIR||serve assets from local filesystem directory [repeatable]|
|-c INT||cache seconds|
|-r /X=/Y||redirect |
|-R /X=/Y||rewrite |
|-l ADDR||listen ip [default 0.0.0.0]|
|-p PORT||listen port [default 8080]|
|-L PATH||log file location|
|-P PATH||pid file location|
|-U INT||daemon set user id|
|-G INT||daemon set group id|
|-B STR||changes brand|
lua server pages
Any files with the extension
.lua will be dynamically
served by redbean. Here's the simplest possible example:
The request handler above should perform at 700,000 responses per second, without any sort of caching. In practice though, it ends up being closer to 300,000 responses per second, since once your response reaches 100 bytes redbean will do things like apply gzip compression automatically.
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
to persist and share state across requests and connections. In the
future, SQLite might be embedded too.
Your Lua interpreter begins its life in the main process at startup in
.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.
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.
/listing page icon, embedded as a base64 URI.
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.
data:URIs that do things like embed a PNG file in a web page. See encodebase64.c.
#fragment. The allowed characters are
-/?.~_@:!$&'()*+,;=0-9A-Za-zand everything else gets
%XXencoded. 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 escapeurlfragment.c.
&><"'. 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.
\uxxxxsequences for all non-ASCII sequences. HTML entities are also encoded, so the output doesn't need
\uxxxxsequences. Ints that are impossible to encode as UTF-16 are substituted with the
\xFFFDreplacement character. See escapejsstringliteral.c.
-.*_0-9A-Za-zand everything else gets
%XXencoded. This function is charset agnostic and will not canonicalize overlong encodings. It is assumed that a UTF-8 string will be supplied. See escapeurlparam.c.
EscapeSegmentexcept slash is allowed. The allowed characters are
-.~_@:!$&'()*+,;=0-9A-Za-z/and everything else gets
%XXencoded. Please note that
'&can still break HTML, so the output may need
EscapeHtmltoo. 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 escapeurlpath.c.
EscapePathexcept slash isn't allowed. The allowed characters are
-.~_@:!$&'()*+,;=0-9A-Za-zand everything else gets
%XXencoded. Please note that
'&can still break HTML, so the output may need
EscapeHtmltoo. 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 escapeurlpathsegment.c.
Mon, 29 Mar 2021 15:37:13 GMT. See formathttpdatetime.c.
Dateheader, which is now, give or take a second. The returned value is a UNIX timestamp.
nameis case-insensitive. The header
valueis returned as a canonical UTF-8 string, with leading and trailing whitespace trimmed, which was decoded from ISO-8859-1. The returned value might contain separators, C0, C1, and even
NULcharacters. It is the responsibility of the caller to impose restrictions on validity if they're desired, since each specific header has its own rules for serialization. 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.
Content-Type) will always have their casing normalized to what the RFC uses. Non-standard headers might have whatever casing the user specifies. This function is suboptimally designed and therefore likely to change in the future. Please consider using
GetHeaderinstead if possible.
OPTIONS. Everything else is rejected by the transport layer, using either a
501 Not Implementedresponse.
nameis handled in a case-sensitive manner. This function checks Request-URI parameters first. Then it checks
application/x-www-form-urlencodedfrom the message body, if it exists, which is common for HTML forms sending
POSTrequests. If a parameter is supplied matching name that has no value, e.g.
?foo&bar=value, then the returned value will be
nil, whereas for
?foo=&bar=valueit would be
"". To differentiate between no-equal and absent, use the
HasParamfunction. The returned value is decoded from ISO-8859-1 (only in the case of Request-URI) 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,
NULcharacters, 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.
application/x-www-form-urlencodedmessage 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.
"/". It is further guaranteed that no
"/."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,
NULcharacters, 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.
-p 0was supplied as the listening port, then the port in this string will be whatever number the operating system assigned.
/listing page to not display any paths beginning with prefix. This function should only be called from
-Dflag was used. If slurping large file into memory is a concern, then consider using
ServeAssetwhich can serve directly off disk.
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 LOGFILEflag is passed, then this will log to the file. Reasonable values for
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.
Mon, 29 Mar 2021 15:37:13 GMTto a UNIX timestamp. See parsehttpdatetime.c.
Serverheader, 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
Expiresheader 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
-zflag to stdout.
308then a redirect response will be sent to the client. This should only be called from
-Dis used. This function is mutually exclusive with
nameis case-insensitive and restricted to non-space ASCII.
valueis 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. The header buffer is independent of the payload buffer. Neither are written to the wire until the Lua Server Page has finished executing. This function disallows the setting of certain headers such as
Content-Rangewhich are abstracted by the transport layer. In such cases, consider calling
reasonis optional since redbean can fill in the appropriate text for well-known magic numbers, e.g.
404, etc. This method will reset the response and is therefore mutually exclusive with
ServeError. If a status setting function isn't called, then the default behavior is to send
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
$(pidof redbean.com) will instruct redbean to run the code
.reload.lua from the main process,
will will be lazily propagated to client connections.
redbean will grow to whatever number of processes your system limits and
tcp stack configuration allow. Once functions like
start to fail, redbean will enter meltdown mode where it begins
interrupting processes so that idle connections can close as quickly as
possible, while sending
503 Service Unavailable responses
from the main process until congestion subsides. This means that redbean
can be a good citizen in large systems where things like quotas and
intelligent load balancing apply.
redbean works best right now for use cases that involve a lot of small messages. For example, pipelining is supported which basically gives us HTTP/2-like performance using just HTTP/1.1. On the other hand, redbean currently doesn't have APIs for handling things like large file uploads. There's a 64kb limit on request message size, so that processes can stay tiny. redbean also won't service hidden files, directory traversalals, or unreasonably fragmented messages. 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.
redbean is designed to be an unsecure webserver. That means redbean is neither secure nor insecure. How much security you want to bolt on is entirely up to you. For example, HTTPS support can be trivially added by having TCP connections flow through a program like stunnel. redbean can also be compiled fully hardened with ASAN memory safety, since the Cosmopolitan C library has the only open source implementation of the Address Sanitizer runtime that's designed for production use.
# Note: Benchmarked on an Intel® Core™ i9-9900 CPU $ wrk -H 'Accept-Encoding: gzip' -t 12 -c 120 \ http://127.0.0.1:8080/tool/net/redbean.html Running 10s test @ http://127.0.0.1:8080/tool/net/redbean.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
Thanks for using redbean! If you like what you've seen and want to encourage more, then consider becoming a GitHub sponsor. PayPal donations are also accepted at firstname.lastname@example.org.
redbean hacker news thread (1,998 upvotes)
αcτµαlly pδrταblε εxεcµταblε (671 upvotes)
justine's web page