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.
this is an old version
click here for the latest redbean
redbean-1.3.com
1.1mb - PE+ELF+MachO+ZIP+SH
93b8fd97ebcfcedaf97a46fe193da96cb8b7ca559aa5e5ab21d00e59a7583526
redbean-1.3.com.dbg
8.3mb - ELF debugger data (optional)
7d17af75521b637ab9cdb3b2e3b8c9847a277fba3dac94d1066b7254c96b2d26
redbean.c
source code
redbean-demo-1.3.com (live demo at http://redbean.justine.lol/)
1.2mb - PE+ELF+MachO+ZIP+SH
bf423ac127ccc17bca3ccbf3a37833ee25418721ebe3c3b824038691c635d402
redbean-demo-1.3.com.dbg
8.3m - ELF debugger data (optional)
7d17af75521b637ab9cdb3b2e3b8c9847a277fba3dac94d1066b7254c96b2d26
redbean-asan-1.3.com (huge but nearly memory safe)
3mb - PE+ELF+MachO+ZIP+SH
2a793e9d9ba3433adaafb910448520adac389953b975694d3be2eb862eccdee5
redbean-asan-1.3.com.dbg
12.5mb - ELF debugger data (optional)
4682c43904fa34fb02b70eb308dd2d4977669c7c896a56e3eb6a52d5d3a40504
sqlite3.com (vacuum, analyze, script, etc. your redbean db)
1.7m - PE+ELF+MachO+ZIP+SH
cd3b969977c9bc2b7356312f95390cec1cf75817d055df5b0676b8d675db15d3
sqlite3.com.dbg
12m - ELF debugger data (optional)
7de970fe87e7118047f24bacbcfc228e78e402e3f00bd27dc75e8f557b49543d
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!)
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>
redbean has a default listing page for the root directory like this:
PKZIP offers the best GUI for for editing your redbean:
You can also use the stock file explorer in Windows 10 or any other tool:
One GUI-like feature redbean itself offers is the ability to
LaunchBrowser()
at startup,
which can be called from your
your /.init.lua
file.
git clone https://github.com/jart/cosmopolitan && cd cosmopolitan make -j8 o//tool/net/redbean.com o//tool/net/redbean.com -vv
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
-h | help |
---|---|
-s | increase silence [repeatable] |
-v | increase verbosity [repeatable] |
-d | daemonize |
-u | uniprocess |
-m | log messages |
-b | log message bodies |
-a | log resource usage |
-g | log handler latency |
-f | log worker function calls |
-z | print port [useful with -p0 for automation]
|
-H K:V | sets http header globally [repeatable] |
-t MS | tunes read and write timeouts [default 30000] |
-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]
|
-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 |
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.
/index.lua
or /index.html
file defined.
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.
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.
/
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.
/.init.lua
then redbean will call it at the ealiest possible moment to handover control for all messages (with the exception of OPTIONS *
). See functions like Route
which asks redbean to do its default thing from the handler.
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 ServeError
. If a status setting function isn't called, then the default behavior is to send 200 OK
.
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. 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 Date
and Content-Range
which are abstracted by the transport layer. In such cases, consider calling ServeAsset
.
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.
&><"'
which become &><"'
. 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.
/.init.lua
.
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-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.
\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.
-.*_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.
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.
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.
Mon, 29 Mar 2021 15:37:13 GMT
. See formathttpdatetime.c.
ParseIp
for the inverse operation.
-D
flag is used)
-D
flag is used)
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 it IsPrivateIp or IsPrivateIp.
0x01020304,31337
would represent 1.2.3.4:31337
. Please consider using GetRemoteAddr instead, since the latter takes into consideration reverse proxy scenarios.
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.
Date
header, which is now, give or take a second. The returned value is a UNIX timestamp.
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.
GetHeader
API if possible since it does a better job abstracting these issues.
kLogDebug
>
kLogVerbose
>
kLogInfo
>
kLogWarn
>
kLogError
>
kLogFatal
.
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 &"
.
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.
"/"
. 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.
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.
9
for HTTP/0.9
, 10
for HTTP/1.0
, or 11
for HTTP/1.1
.
application/x-www-form-urlencoded
message body.
/
listing page to not display any paths beginning with prefix. This function should only be called from /.init.lua
.
-D
flag was used. If slurping large file into memory is a concern, then consider using ServeAsset
which 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 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.
Mon, 29 Mar 2021 15:37:13 GMT
to a UNIX timestamp. See parsehttpdatetime.c.
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.
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.
"1.2.3.4" → 0x01020304
, or returns -1
for invalid inputs. See also FormatIp
for the inverse operation.
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
.
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
.
GetServerAddr
or the -z
flag to stdout.
301
, 302
, 307
, or 308
then a redirect response will be sent to the client. This should only be called from /.init.lua
.
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 handover 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.
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.
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.
-D
is used. This function is mutually exclusive with SetStatus
and ServeError
.
SetStatus
and ServeAsset
.
level
are kLogDebug
>
kLogVerbose
>
kLogInfo
>
kLogWarn
>
kLogError
>
kLogFatal
.
Please refer to the LuaSQLite3 Documentation.
For example, you could put the following in
your /.init.lua
file:
sqlite3 = require "lsqlite3" db = sqlite3.open_memory() db:exec[[ CREATE TABLE test ( id INTEGER PRIMARY KEY, content TEXT ); INSERT INTO test (content) VALUES ('Hello World'); INSERT INTO test (content) VALUES ('Hello Lua'); INSERT INTO test (content) VALUES ('Hello Sqlite3'); ]]
Then, your Lua server pages or OnHttpRequest
handler may
perform SQL queries by accessing the db
global. The
performance is good too, at about 400k qps.
for row in db:nrows("SELECT * FROM test") do Write(row.id.." "..row.content.."<br>") end
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.
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.compile
plus regex_t*:search
.
/.init.lua
file. Flags may contain re.BASIC
, re.ICASE
, re.NOSUB
, and/or re.NEWLINE
. See also regcomp() from libc.
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.
([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.compile
and re.search
.
[a-z]
will mean the same thing as [A-Za-z]
. This flag may only be used with re.compile
and re.search
.
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.search
and regex_t*:search
re.search
and regex_t*:search
Log
.
kLogDebug
. See Log
.
kLogVerbose
. See Log
.
kLogVerbose
. See Log
.
kLogWarn
. See Log
.
kLogError
. See Log
.
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.
redbean is designed to be an unsecure webserver (i.e. neither secure nor insecure). You can bolt on those additional layers of security separately. There currently isn't an intent to bake-in HTTPS support since it can be trivially added by having TCP connections flow through a program like stunnel or haproxy.
If you want Rust-like assurances 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.
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 because Windows multiprocessing isn't very good.
No APIs are currently provided for use-cases like long-polling and websockets. That should change in the future.
# 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
Please support redbean by becoming a
GitHub
sponsor.
PayPal donations are accepted at jtunney@gmail.com
redbean hacker news thread (1,998 upvotes)
αcτµαlly pδrταblε εxεcµταblε (671 upvotes)
justine's web page
redbean-1.0.com (540kb, .com.dbg)
redbean 0.4
redbean 0.3