Add HTTP/HTTPS Fetch() API to redbean

You can now say the following in your redbean Lua code:

    status,headers,payload = Fetch("https://foo.example")

The following Lua APIs have been introduced:

  - Fetch(str) → str,{str:str},str
  - GetHttpReason(int) → str
  - GetHttpReason(int) → str
  - ProgramSslFetchVerify(bool)
  - ProgramSslClientVerify(bool)

The following flags have been introduced:

  - `-j` enables client SSL verification
  - `-k` disables Fetch() SSL verification
  - `-t INT` may now be passed a negative value for keepalive

Lua exceptions now invoke Cosmopolitan's garbage collector when
unwinding the stack. So it's now safe to use _gc() w/ Lua 𝔱𝔥𝔯𝔬𝔴

See #97
This commit is contained in:
Justine Tunney 2021-07-07 21:44:27 -07:00
parent 36b2710e1a
commit c89bc56f6a
35 changed files with 1611 additions and 591 deletions

96
tool/build/deltaify.c Normal file
View file

@ -0,0 +1,96 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/ucontext.h"
#include "libc/errno.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
static int pid;
static void RelaySig(int sig, struct siginfo *si, struct ucontext *uc) {
kill(pid, sig);
}
int main(int argc, char *argv[]) {
FILE *f;
char *s;
bool ok;
int64_t micros;
long double t1, t2;
int ws, pipefds[2];
t1 = nowl();
if (argc < 2) {
f = stdin;
} else {
sigset_t block, mask;
struct sigaction saignore = {.sa_handler = SIG_IGN};
struct sigaction sadefault = {.sa_handler = SIG_DFL};
struct sigaction sarelay = {.sa_sigaction = RelaySig,
.sa_flags = SA_RESTART};
sigemptyset(&block);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block, &mask);
sigaction(SIGHUP, &sarelay, 0);
sigaction(SIGTERM, &sarelay, 0);
sigaction(SIGINT, &saignore, 0);
sigaction(SIGQUIT, &saignore, 0);
CHECK_NE(-1, pipe(pipefds));
CHECK_NE(-1, (pid = vfork()));
if (!pid) {
close(pipefds[0]);
dup2(pipefds[1], 1);
sigaction(SIGHUP, &sadefault, NULL);
sigaction(SIGTERM, &sadefault, NULL);
sigaction(SIGINT, &sadefault, NULL);
sigaction(SIGQUIT, &sadefault, NULL);
sigprocmask(SIG_SETMASK, &mask, NULL);
execvp(argv[1], argv + 1);
_exit(127);
}
close(pipefds[1]);
sigprocmask(SIG_SETMASK, &mask, NULL);
CHECK((f = fdopen(pipefds[0], "r")));
CHECK_NE(-1, setvbuf(f, malloc(4096), _IOLBF, 4096));
}
for (;;) {
if (!(s = xgetline(f))) break;
t2 = nowl();
micros = (t2 - t1) * 1e6;
t1 = t2;
printf("%,16ld %s", micros, s);
free(s);
}
ok = !ferror(f);
if (argc < 2) return ok ? 0 : 1;
fclose(f);
while (waitpid(pid, &ws, 0) == -1) CHECK_EQ(EINTR, errno);
return !WIFEXITED(ws) ? 128 + WTERMSIG(ws) : ok ? WEXITSTATUS(ws) : 1;
}

View file

@ -84,12 +84,14 @@ C(sslcantciphers)
C(sslhandshakefails)
C(sslhandshakes)
C(sslnociphers)
C(sslnoclientcert)
C(sslnoversion)
C(sslshakemacs)
C(ssltimeouts)
C(sslunknownca)
C(sslunknowncert)
C(sslupgrades)
C(sslverifyfailed)
C(statfails)
C(staticrequests)
C(stats)

74
tool/net/demo/fetch.lua Normal file
View file

@ -0,0 +1,74 @@
-- Fetch() API Demo
local function WriteForm(url)
Write('<!doctype html>\r\n')
Write(string.format([[
<title>redbean fetch demo</title>
<style>
body {
padding: 1em;
}
h1 a {
color: inherit;
text-decoration: none;
}
h1 img {
vertical-align: middle;
}
input {
margin: 1em;
padding: .5em;
}
p {
word-break: break-word;
}
p span {
display: block;
text-indent: -1em;
padding-left: 1em;
}
</style>
<h1>
<a href="/"><img src="/redbean.png"></a>
<a href="fetch.lua">redbean fetch demo</a>
</h1>
<form action="fetch.lua" method="post">
<input type="text" id="url" name="url" size="70"
value="%s" placeholder="uri" autofocus>
<input type="submit" value="fetch">
</form>
]], EscapeHtml(url)))
end
local function main()
if IsPublicIp(GetClientAddr()) then
ServeError(403)
elseif GetMethod() == 'GET' or GetMethod() == 'HEAD' then
WriteForm("https://www.cloudflare.com/robots.txt")
elseif GetMethod() == 'POST' then
status, headers, payload = Fetch(GetParam('url'))
WriteForm(GetParam('url'))
Write('<dl>\r\n')
Write('<dt>Status\r\n')
Write(string.format('<dd><p>%d %s\r\n', status, GetHttpReason(status)))
Write('<dt>Headers\r\n')
Write('<dd><p>\r\n')
for k,v in pairs(headers) do
Write('<span><strong>')
Write(EscapeHtml(k))
Write('</strong>: ')
Write(EscapeHtml(v))
Write('</span>\r\n')
end
Write('<dt>Payload\r\n')
Write('<dd><pre>')
Write(EscapeHtml(VisualizeControlCodes(payload)))
Write('</pre>\r\n')
Write('</dl>\r\n')
else
ServeError(405)
SetHeader('Allow', 'GET, HEAD, POST')
end
end
main()

View file

@ -1,2 +1,4 @@
StoreAsset('/hi', 'sup')
if not IsPublicIp(GetClientAddr()) then
StoreAsset('/hi', 'sup')
end
mymodule.hello()

View file

@ -34,8 +34,10 @@ FLAGS
-b log message bodies
-a log resource usage
-g log handler latency
-f log worker function calls
-j enable ssl client verify
-k disable ssl fetch verify
-B use stronger cryptography
-f log worker function calls
-s increase silence [repeatable]
-v increase verbosity [repeatable]
-V increase ssl verbosity [repeatable]
@ -45,8 +47,8 @@ FLAGS
-R /X=/Y rewrites X to Y [repeatable]
-K PATH tls private key path [repeatable]
-C PATH tls certificate(s) path [repeatable]
-t MS tunes read and write timeouts [def. 60000]
-M INT tunes max message payload size [def. 65536]
-t INT timeout ms or keepalive sec if <0 [def. 60000]
-p PORT listen port [def. 8080; repeatable]
-l ADDR listen addr [def. 0.0.0.0; repeatable]
-c SEC configures static cache-control

View file

@ -50,6 +50,7 @@ TOOL_NET_DIRECTDEPS = \
LIBC_X \
LIBC_ZIPOS \
NET_HTTP \
NET_HTTPS \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LUA \
@ -130,6 +131,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
tool/net/demo/seekable.txt \
tool/net/demo/virtualbean.html \
tool/net/demo/printpayload.lua \
tool/net/demo/fetch.lua \
tool/net/redbean.c \
net/http/parsehttpmessage.c \
net/http/parseurl.c \
@ -145,10 +147,10 @@ o/$(MODE)/tool/net/redbean-demo.com: \
@(cd o/$(MODE)/tool/net && ../../host/third_party/infozip/zip.com -qr redbean-demo.com .lua)
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/hello.lua tool/net/demo/sql.lua
@echo "&lt;-- check out this lua server page" | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj $@ tool/net/demo/redbean.lua
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua tool/net/demo/printpayload.lua
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua tool/net/demo/printpayload.lua tool/net/demo/fetch.lua
@echo Uncompressed for HTTP Range requests | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj0 $@ tool/net/demo/seekable.txt
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -q $@ tool/net/ tool/net/demo/ tool/net/demo/index.html tool/net/demo/redbean.css tool/net/redbean.c net/http/parsehttpmessage.c net/http/parseurl.c net/http/encodeurl.c test/net/http/parsehttpmessage_test.c test/net/http/parseurl_test.c
@printf "<p>This is a live instance of <a href=https://justine.lol/redbean/>redbean</a>: a tiny multiplatform webserver that <a href=https://news.ycombinator.com/item?id=26271117>went viral</a> on hacker news a few months ago.\r\nSince then, we've added Lua dynamic serving, which also goes as fast as 1,000,000 requests per second on a core i9 (rather than a cheap virtual machine like this)\nin addition to SQLite and SSL. The text you're reading now is a PKZIP End Of Central Directory comment.\r\n<p>redbean aims to be production worthy across six operating systems, using a single executable file (this demo is hosted on FreeBSD 13). redbean has been enhanced to restore the APE header after startup.\r\nIt automatically generates this listing page based on your zip contents. If you use redbean as an application server / web development environment,\r\nthen you'll find other new and useful features like function call logging so you can get that sweet sweet microsecond scale latency." | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -z $@
@printf "<p>Thank you for using <a href=https://justine.lol/redbean/>redbean</a> the tiniest most vertically integrated actually portable web server with superior performance." | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -z $@
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/virtualbean.justine.lol/
@$(COMPILE) -ACP -T$@ cp tool/net/redbean.png o/$(MODE)/tool/net/virtualbean.justine.lol/redbean.png
@$(COMPILE) -ACP -T$@ cp tool/net/demo/virtualbean.html o/$(MODE)/tool/net/virtualbean.justine.lol/index.html

File diff suppressed because it is too large Load diff