Fix bugs and make improvements to redbean

- Abort if .init.lua fails
- Refactor redbean to use new append library
- Use first certificate if SNI routing fails
- Use function/data sections when building Lua
- Don't use self-signed auto-generated cert for client
- Add -D staging dirs to redbean lua module default path
This commit is contained in:
Justine Tunney 2021-08-06 14:12:11 -07:00
parent 55a15c204e
commit aeeb851422
26 changed files with 703 additions and 513 deletions

View file

@ -1,3 +1,3 @@
SetStatus(200)
SetHeader("Content-Type", GetHeader("Content-Type"))
SetHeader("Content-Type", GetHeader("Content-Type") or "text/plain")
Write(GetPayload())

View file

@ -244,7 +244,8 @@ SECURITY
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.
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.
Your redbean has been secured with algorithms so strong that, until a
few decades ago, it was illegal to share them with with those outside

View file

@ -151,7 +151,6 @@ o/$(MODE)/tool/net/redbean-demo.com: \
@$(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>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

View file

@ -51,6 +51,7 @@
#include "libc/runtime/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/append.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/undeflate.h"
@ -91,6 +92,7 @@
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/ltests.h"
#include "third_party/lua/lua.h"
#include "third_party/lua/luaconf.h"
#include "third_party/lua/lualib.h"
#include "third_party/mbedtls/asn1.h"
#include "third_party/mbedtls/asn1write.h"
@ -352,6 +354,7 @@ static uint32_t clientaddrsize;
static lua_State *L;
static size_t zsize;
static char *outbuf;
static char *content;
static uint8_t *zmap;
static uint8_t *zbase;
@ -384,7 +387,6 @@ static ssize_t (*generator)(struct iovec[3]);
static struct Buffer inbuf;
static struct Buffer oldin;
static struct Buffer hdrbuf;
static struct Buffer outbuf;
static struct timeval timeout;
static struct Buffer effectivepath;
@ -526,57 +528,14 @@ static void CollectGarbage(void) {
}
static void UseOutput(void) {
content = FreeLater(outbuf.p);
contentlength = outbuf.n;
outbuf.p = 0;
outbuf.n = 0;
outbuf.c = 0;
content = FreeLater(outbuf);
contentlength = appendz(outbuf).i;
outbuf = 0;
}
static void DropOutput(void) {
FreeLater(outbuf.p);
outbuf.p = 0;
outbuf.n = 0;
outbuf.c = 0;
}
static void ClearOutput(void) {
outbuf.n = 0;
}
static void Grow(size_t n) {
do {
if (outbuf.c) {
outbuf.c += outbuf.c >> 1;
} else {
outbuf.c = 16 * 1024;
}
} while (n > outbuf.c);
outbuf.p = xrealloc(outbuf.p, outbuf.c);
}
static void AppendData(const char *data, size_t size) {
size_t n;
n = outbuf.n + size;
if (n > outbuf.c) Grow(n);
memcpy(outbuf.p + outbuf.n, data, size);
outbuf.n = n;
}
static void Append(const char *fmt, ...) {
int n;
char *p;
va_list va, vb;
va_start(va, fmt);
va_copy(vb, va);
n = vsnprintf(outbuf.p + outbuf.n, outbuf.c - outbuf.n, fmt, va);
if (n >= outbuf.c - outbuf.n) {
Grow(outbuf.n + n + 1);
vsnprintf(outbuf.p + outbuf.n, outbuf.c - outbuf.n, fmt, vb);
}
va_end(vb);
va_end(va);
outbuf.n += n;
FreeLater(outbuf);
outbuf = 0;
}
static char *MergePaths(const char *p, size_t n, const char *q, size_t m,
@ -641,9 +600,11 @@ static mbedtls_x509_crt *GetTrustedCertificate(mbedtls_x509_name *name) {
return 0;
}
static void UseCertificate(mbedtls_ssl_config *c, struct Cert *kp) {
VERBOSEF("using %s certificate %`'s", mbedtls_pk_get_name(&kp->cert->pk),
gc(FormatX509Name(&kp->cert->subject)));
static void UseCertificate(mbedtls_ssl_config *c, struct Cert *kp,
const char *role) {
VERBOSEF("using %s certificate %`'s for HTTPS %s",
mbedtls_pk_get_name(&kp->cert->pk),
gc(FormatX509Name(&kp->cert->subject)), role);
CHECK_EQ(0, mbedtls_ssl_conf_own_cert(c, kp->cert, kp->key));
}
@ -947,8 +908,8 @@ static void ProgramTimeout(long ms) {
timeout.tv_sec = ms; /* -(keepalive seconds) */
timeout.tv_usec = 0;
} else {
if (ms <= 30) {
fprintf(stderr, "error: timeout needs to be 31ms or greater\n");
if (ms < 10) {
fprintf(stderr, "error: timeout needs to be 10ms or greater\n");
exit(1);
}
d = ldiv(ms, 1000);
@ -1168,57 +1129,57 @@ static void ReportWorkerExit(int pid, int ws) {
}
}
static void AppendResourceReport(struct rusage *ru, const char *nl) {
static void AppendResourceReport(char **b, struct rusage *ru, const char *nl) {
long utime, stime;
long double ticks;
if (ru->ru_maxrss) {
Append("ballooned to %,ldkb in size%s", ru->ru_maxrss, nl);
appendf(b, "ballooned to %,ldkb in size%s", ru->ru_maxrss, nl);
}
if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) |
(stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) {
ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK));
Append("needed %,ldµs cpu (%d%% kernel)%s", utime + stime,
(int)((long double)stime / (utime + stime) * 100), nl);
appendf(b, "needed %,ldµs cpu (%d%% kernel)%s", utime + stime,
(int)((long double)stime / (utime + stime) * 100), nl);
if (ru->ru_idrss) {
Append("needed %,ldkb memory on average%s", lroundl(ru->ru_idrss / ticks),
nl);
appendf(b, "needed %,ldkb memory on average%s",
lroundl(ru->ru_idrss / ticks), nl);
}
if (ru->ru_isrss) {
Append("needed %,ldkb stack on average%s", lroundl(ru->ru_isrss / ticks),
nl);
appendf(b, "needed %,ldkb stack on average%s",
lroundl(ru->ru_isrss / ticks), nl);
}
if (ru->ru_ixrss) {
Append("mapped %,ldkb shared on average%s", lroundl(ru->ru_ixrss / ticks),
nl);
appendf(b, "mapped %,ldkb shared on average%s",
lroundl(ru->ru_ixrss / ticks), nl);
}
}
if (ru->ru_minflt || ru->ru_majflt) {
Append("caused %,ld page faults (%d%% memcpy)%s",
ru->ru_minflt + ru->ru_majflt,
(int)((long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) *
100),
nl);
appendf(b, "caused %,ld page faults (%d%% memcpy)%s",
ru->ru_minflt + ru->ru_majflt,
(int)((long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) *
100),
nl);
}
if (ru->ru_nvcsw + ru->ru_nivcsw > 1) {
Append(
"%,ld context switches (%d%% consensual)%s",
appendf(
b, "%,ld context switches (%d%% consensual)%s",
ru->ru_nvcsw + ru->ru_nivcsw,
(int)((long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100),
nl);
}
if (ru->ru_msgrcv || ru->ru_msgsnd) {
Append("received %,ld message%s and sent %,ld%s", ru->ru_msgrcv,
ru->ru_msgrcv == 1 ? "" : "s", ru->ru_msgsnd, nl);
appendf(b, "received %,ld message%s and sent %,ld%s", ru->ru_msgrcv,
ru->ru_msgrcv == 1 ? "" : "s", ru->ru_msgsnd, nl);
}
if (ru->ru_inblock || ru->ru_oublock) {
Append("performed %,ld read%s and %,ld write i/o operations%s",
ru->ru_inblock, ru->ru_inblock == 1 ? "" : "s", ru->ru_oublock, nl);
appendf(b, "performed %,ld read%s and %,ld write i/o operations%s",
ru->ru_inblock, ru->ru_inblock == 1 ? "" : "s", ru->ru_oublock, nl);
}
if (ru->ru_nsignals) {
Append("received %,ld signals%s", ru->ru_nsignals, nl);
appendf(b, "received %,ld signals%s", ru->ru_nsignals, nl);
}
if (ru->ru_nswap) {
Append("got swapped %,ld times%s", ru->ru_nswap, nl);
appendf(b, "got swapped %,ld times%s", ru->ru_nswap, nl);
}
}
@ -1251,16 +1212,16 @@ static void AddRusage(struct rusage *x, const struct rusage *y) {
}
static void ReportWorkerResources(int pid, struct rusage *ru) {
const char *s;
char *s, *b = 0;
if (logrusage || LOGGABLE(kLogDebug)) {
AppendResourceReport(ru, "\n");
if (outbuf.n) {
if ((s = IndentLines(outbuf.p, outbuf.n - 1, 0, 1))) {
AppendResourceReport(&b, ru, "\n");
if (b) {
if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) {
flogf(kLogInfo, __FILE__, __LINE__, NULL,
"resource report for pid %d\n%s", pid, s);
free(s);
}
ClearOutput();
free(b);
}
}
}
@ -1512,68 +1473,101 @@ static void WipeServingKeys(void) {
/* mbedtls_ssl_key_cert_free(conf.key_cert); */
}
static int TlsRouteCertificate(mbedtls_ssl_context *ssl, int i,
const unsigned char *host, size_t size) {
int rc;
if (!(rc = mbedtls_ssl_set_hs_own_cert(ssl, certs.p[i].cert,
certs.p[i].key))) {
DEBUGF("TlsRoute(%`'.*s) %s %`'s", size, host,
mbedtls_pk_get_name(&certs.p[i].cert->pk),
gc(FormatX509Name(&certs.p[i].cert->subject)));
return 0;
} else {
return -1;
static bool CertHasCommonName(const mbedtls_x509_crt *cert,
const unsigned char *host, size_t size) {
const mbedtls_x509_name *name;
for (name = &cert->subject; name; name = name->next) {
if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) {
if (SlicesEqualCase(host, size, name->val.p, name->val.len)) {
return true;
}
break;
}
}
return false;
}
static bool CertHasHost(const mbedtls_x509_crt *cert, const unsigned char *host,
size_t size) {
const mbedtls_x509_sequence *cur;
for (cur = &cert->subject_alt_names; cur; cur = cur->next) {
if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) ==
MBEDTLS_X509_SAN_DNS_NAME &&
SlicesEqualCase(host, size, cur->buf.p, cur->buf.len)) {
return true;
}
}
return false;
}
static bool CertHasIp(const mbedtls_x509_crt *cert, uint32_t ip) {
const mbedtls_x509_sequence *cur;
for (cur = &cert->subject_alt_names; cur; cur = cur->next) {
if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) ==
MBEDTLS_X509_SAN_IP_ADDRESS &&
cur->buf.len == 4 && ip == READ32BE(cur->buf.p)) {
return true;
}
}
return false;
}
static bool IsServerCert(mbedtls_pk_type_t type, int i) {
return certs.p[i].cert && certs.p[i].key && !certs.p[i].cert->ca_istrue &&
mbedtls_pk_get_type(certs.p[i].key) == type &&
!mbedtls_x509_crt_check_extended_key_usage(
certs.p[i].cert, MBEDTLS_OID_SERVER_AUTH,
MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH));
}
static bool TlsRouteFind(mbedtls_pk_type_t type, mbedtls_ssl_context *ssl,
const unsigned char *host, size_t size, int64_t ip) {
int i;
for (i = 0; i < certs.n; ++i) {
if (IsServerCert(type, i) &&
(((certs.p[i].cert->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) &&
(ip == -1 ? CertHasHost(certs.p[i].cert, host, size)
: CertHasIp(certs.p[i].cert, ip))) ||
CertHasCommonName(certs.p[i].cert, host, size))) {
CHECK_EQ(
0, mbedtls_ssl_set_hs_own_cert(ssl, certs.p[i].cert, certs.p[i].key));
DEBUGF("TlsRoute(%s, %`'.*s) %s %`'s", mbedtls_pk_type_name(type), size,
host, mbedtls_pk_get_name(&certs.p[i].cert->pk),
gc(FormatX509Name(&certs.p[i].cert->subject)));
return true;
}
}
return false;
}
static bool TlsRouteFirst(mbedtls_pk_type_t type, mbedtls_ssl_context *ssl) {
int i;
for (i = 0; i < certs.n; ++i) {
if (IsServerCert(type, i)) {
CHECK_EQ(
0, mbedtls_ssl_set_hs_own_cert(ssl, certs.p[i].cert, certs.p[i].key));
DEBUGF("TlsRoute(%s) %s %`'s", mbedtls_pk_type_name(type),
mbedtls_pk_get_name(&certs.p[i].cert->pk),
gc(FormatX509Name(&certs.p[i].cert->subject)));
return true;
}
}
return false;
}
static int TlsRoute(void *ctx, mbedtls_ssl_context *ssl,
const unsigned char *host, size_t size) {
int rc;
size_t i;
int64_t ip;
int santype;
const mbedtls_x509_name *name;
const mbedtls_x509_sequence *cur;
bool ok1, ok2;
ip = ParseIp((const char *)host, size);
for (rc = -1, i = 0; i < certs.n; ++i) {
if (!certs.p[i].key || !certs.p[i].cert || certs.p[i].cert->ca_istrue ||
mbedtls_x509_crt_check_extended_key_usage(
certs.p[i].cert, MBEDTLS_OID_SERVER_AUTH,
MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH))) {
continue;
}
if (ip == -1) {
if (certs.p[i].cert->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) {
for (cur = &certs.p[i].cert->subject_alt_names; cur; cur = cur->next) {
if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) ==
MBEDTLS_X509_SAN_DNS_NAME &&
SlicesEqualCase(host, size, cur->buf.p, cur->buf.len)) {
if (!TlsRouteCertificate(ssl, i, host, size)) rc = 0;
break;
}
}
} else {
for (name = &certs.p[i].cert->subject; name; name = name->next) {
if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) &&
SlicesEqualCase(host, size, name->val.p, name->val.len)) {
if (!TlsRouteCertificate(ssl, i, host, size)) rc = 0;
break;
}
}
}
} else if (certs.p[i].cert->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) {
for (cur = &certs.p[i].cert->subject_alt_names; cur; cur = cur->next) {
if ((cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) ==
MBEDTLS_X509_SAN_IP_ADDRESS &&
cur->buf.len == 4 && ip == READ32BE(cur->buf.p)) {
if (!TlsRouteCertificate(ssl, i, host, size)) rc = 0;
break;
}
}
}
ok1 = TlsRouteFind(MBEDTLS_PK_ECKEY, ssl, host, size, ip);
ok2 = TlsRouteFind(MBEDTLS_PK_RSA, ssl, host, size, ip);
if (!ok1 && !ok2) {
VERBOSEF("TlsRoute(%`'.*s) not found", size, host);
ok1 = TlsRouteFirst(MBEDTLS_PK_ECKEY, ssl);
ok2 = TlsRouteFirst(MBEDTLS_PK_RSA, ssl);
}
if (rc) VERBOSEF("TlsRoute(%`'.*s) not found", size, host);
return rc;
return ok1 || ok2 ? 0 : -1;
}
static bool TlsSetup(void) {
@ -1872,19 +1866,19 @@ static void LoadCertificates(void) {
certs.p[i].cert, MBEDTLS_OID_SERVER_AUTH,
MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH))) {
LogCertificate("using server certificate", certs.p[i].cert);
UseCertificate(&conf, certs.p + i);
UseCertificate(&conf, certs.p + i, "server");
havecert = true;
}
if (!mbedtls_x509_crt_check_extended_key_usage(
certs.p[i].cert, MBEDTLS_OID_CLIENT_AUTH,
MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH))) {
LogCertificate("using client certificate", certs.p[i].cert);
UseCertificate(&confcli, certs.p + i);
UseCertificate(&confcli, certs.p + i, "client");
haveclientcert = true;
}
}
}
if (!havecert || !haveclientcert) {
if (!havecert) {
if ((ksk = GetKeySigningKey()).key) {
DEBUGF("generating ssl certificates using %`'s",
gc(FormatX509Name(&ksk.cert->subject)));
@ -1898,14 +1892,14 @@ static void LoadCertificates(void) {
}
#ifdef MBEDTLS_ECP_C
ecp = GenerateEcpCertificate(ksk.key ? &ksk : 0);
if (!havecert) UseCertificate(&conf, &ecp);
if (!haveclientcert) UseCertificate(&confcli, &ecp);
if (!havecert) UseCertificate(&conf, &ecp, "server");
if (!haveclientcert && ksk.key) UseCertificate(&confcli, &ecp, "client");
AppendCert(ecp.cert, ecp.key);
#endif
#ifdef MBEDTLS_RSA_C
rsa = GenerateRsaCertificate(ksk.key ? &ksk : 0);
if (!havecert) UseCertificate(&conf, &rsa);
if (!haveclientcert) UseCertificate(&confcli, &rsa);
if (!havecert) UseCertificate(&conf, &rsa, "server");
if (!haveclientcert && ksk.key) UseCertificate(&confcli, &rsa, "client");
AppendCert(rsa.cert, rsa.key);
#endif
}
@ -2409,11 +2403,12 @@ static void AppendLogo(void) {
char *p, *q;
struct Asset *a;
if ((a = GetAsset("/redbean.png", 12)) && (p = LoadAsset(a, &n))) {
q = EncodeBase64(p, n, &n);
Append("<img alt=\"[logo]\" src=\"data:image/png;base64,");
AppendData(q, n);
Append("\">\r\n");
free(q);
if ((q = EncodeBase64(p, n, &n))) {
appends(&outbuf, "<img alt=\"[logo]\" src=\"data:image/png;base64,");
appendd(&outbuf, q, n);
appends(&outbuf, "\">\r\n");
free(q);
}
free(p);
}
}
@ -2438,15 +2433,17 @@ static ssize_t Send(struct iovec *iov, int iovlen) {
static char *CommitOutput(char *p) {
uint32_t crc;
size_t outbuflen;
if (!contentlength) {
if (istext && outbuf.n >= 100) {
outbuflen = appendz(outbuf).i;
if (istext && outbuflen >= 100) {
p = stpcpy(p, "Vary: Accept-Encoding\r\n");
if (!IsTiny() && ClientAcceptsGzip()) {
gzipped = true;
crc = crc32_z(0, outbuf.p, outbuf.n);
crc = crc32_z(0, outbuf, outbuflen);
WRITE32LE(gzip_footer + 0, crc);
WRITE32LE(gzip_footer + 4, outbuf.n);
content = FreeLater(Deflate(outbuf.p, outbuf.n, &contentlength));
WRITE32LE(gzip_footer + 4, outbuflen);
content = FreeLater(Deflate(outbuf, outbuflen, &contentlength));
DropOutput();
} else {
UseOutput();
@ -2463,11 +2460,11 @@ static char *CommitOutput(char *p) {
static char *ServeDefaultErrorPage(char *p, unsigned code, const char *reason) {
p = AppendContentType(p, "text/html; charset=ISO-8859-1");
reason = FreeLater(EscapeHtml(reason, -1, 0));
Append("\
appends(&outbuf, "\
<!doctype html>\r\n\
<title>");
Append("%d %s", code, reason);
Append("\
appendf(&outbuf, "%d %s", code, reason);
appendf(&outbuf, "\
</title>\r\n\
<style>\r\n\
html { color: #111; font-family: sans-serif; }\r\n\
@ -2475,8 +2472,8 @@ img { vertical-align: middle; }\r\n\
</style>\r\n\
<h1>\r\n");
AppendLogo();
Append("%d %s\r\n", code, reason);
Append("</h1>\r\n");
appendf(&outbuf, "%d %s\r\n", code, reason);
appends(&outbuf, "</h1>\r\n");
UseOutput();
return p;
}
@ -2486,7 +2483,7 @@ static char *ServeErrorImpl(unsigned code, const char *reason) {
char *p, *s;
struct Asset *a;
LockInc(&shared->c.errors);
ClearOutput();
DropOutput();
p = SetStatus(code, reason);
s = xasprintf("/%d.html", code);
a = GetAsset(s, strlen(s));
@ -2868,7 +2865,7 @@ static char *ServeListing(void) {
size_t i, n, pathlen, rn[6];
LockInc(&shared->c.listingrequests);
if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod();
Append("\
appends(&outbuf, "\
<!doctype html>\r\n\
<meta charset=\"utf-8\">\r\n\
<title>redbean zip listing</title>\r\n\
@ -2885,15 +2882,16 @@ td { padding-right: 3em; }\r\n\
<header><h1>\r\n");
AppendLogo();
rp[0] = EscapeHtml(brand, -1, &rn[0]);
AppendData(rp[0], rn[0]);
appendd(&outbuf, rp[0], rn[0]);
free(rp[0]);
Append("</h1>\r\n"
"<div class=\"eocdcomment\">%.*s</div>\r\n"
"<hr>\r\n"
"</header>\r\n"
"<pre>\r\n",
strnlen(GetZipCdirComment(zcdir), GetZipCdirCommentSize(zcdir)),
GetZipCdirComment(zcdir));
appendf(&outbuf,
"</h1>\r\n"
"<div class=\"eocdcomment\">%.*s</div>\r\n"
"<hr>\r\n"
"</header>\r\n"
"<pre>\r\n",
strnlen(GetZipCdirComment(zcdir), GetZipCdirCommentSize(zcdir)),
GetZipCdirComment(zcdir));
memset(w, 0, sizeof(w));
n = GetZipCdirRecords(zcdir);
for (zcf = zbase + GetZipCdirOffset(zcdir); n--;
@ -2925,14 +2923,16 @@ td { padding-right: 3em; }\r\n\
iso8601(tb, &tm);
if (IsCompressionMethodSupported(ZIP_CFILE_COMPRESSIONMETHOD(zcf)) &&
IsAcceptablePath(path, pathlen)) {
Append("<a href=\"%.*s\">%-*.*s</a> %s %0*o %4s %,*ld %'s\r\n",
rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1], GetZipCfileMode(zcf),
DescribeCompressionRatio(rb, zcf), w[2],
GetZipCfileUncompressedSize(zcf), rp[3]);
appendf(&outbuf,
"<a href=\"%.*s\">%-*.*s</a> %s %0*o %4s %,*ld %'s\r\n",
rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1],
GetZipCfileMode(zcf), DescribeCompressionRatio(rb, zcf), w[2],
GetZipCfileUncompressedSize(zcf), rp[3]);
} else {
Append("%-*.*s %s %0*o %4s %,*ld %'s\r\n", w[0], rn[4], rp[4], tb,
w[1], GetZipCfileMode(zcf), DescribeCompressionRatio(rb, zcf),
w[2], GetZipCfileUncompressedSize(zcf), rp[3]);
appendf(&outbuf, "%-*.*s %s %0*o %4s %,*ld %'s\r\n", w[0], rn[4],
rp[4], tb, w[1], GetZipCfileMode(zcf),
DescribeCompressionRatio(rb, zcf), w[2],
GetZipCfileUncompressedSize(zcf), rp[3]);
}
free(rp[4]);
free(rp[3]);
@ -2942,42 +2942,44 @@ td { padding-right: 3em; }\r\n\
}
free(path);
}
Append("</pre><footer><hr>\r\n");
Append("<table border=\"0\"><tr>\r\n");
Append("<td valign=\"top\">\r\n");
Append("<a href=\"/statusz\">/statusz</a>\r\n");
appends(&outbuf, "\
</pre><footer><hr>\r\n\
<table border=\"0\"><tr>\r\n\
<td valign=\"top\">\r\n\
<a href=\"/statusz\">/statusz</a>\r\n\
");
if (shared->c.connectionshandled) {
Append("says your redbean<br>\r\n");
AppendResourceReport(&shared->children, "<br>\r\n");
appends(&outbuf, "says your redbean<br>\r\n");
AppendResourceReport(&outbuf, &shared->children, "<br>\r\n");
}
Append("<td valign=\"top\">\r\n");
appends(&outbuf, "<td valign=\"top\">\r\n");
and = "";
x = nowl() - startserver;
y = ldiv(x, 24L * 60 * 60);
if (y.quot) {
Append("%,ld day%s ", y.quot, y.quot == 1 ? "" : "s");
appendf(&outbuf, "%,ld day%s ", y.quot, y.quot == 1 ? "" : "s");
and = "and ";
}
y = ldiv(y.rem, 60 * 60);
if (y.quot) {
Append("%,ld hour%s ", y.quot, y.quot == 1 ? "" : "s");
appendf(&outbuf, "%,ld hour%s ", y.quot, y.quot == 1 ? "" : "s");
and = "and ";
}
y = ldiv(y.rem, 60);
if (y.quot) {
Append("%,ld minute%s ", y.quot, y.quot == 1 ? "" : "s");
appendf(&outbuf, "%,ld minute%s ", y.quot, y.quot == 1 ? "" : "s");
and = "and ";
}
Append("%s%,ld second%s of operation<br>\r\n", and, y.rem,
y.rem == 1 ? "" : "s");
appendf(&outbuf, "%s%,ld second%s of operation<br>\r\n", and, y.rem,
y.rem == 1 ? "" : "s");
x = shared->c.messageshandled;
Append("%,ld message%s handled<br>\r\n", x, x == 1 ? "" : "s");
appendf(&outbuf, "%,ld message%s handled<br>\r\n", x, x == 1 ? "" : "s");
x = shared->c.connectionshandled;
Append("%,ld connection%s handled<br>\r\n", x, x == 1 ? "" : "s");
appendf(&outbuf, "%,ld connection%s handled<br>\r\n", x, x == 1 ? "" : "s");
x = shared->workers;
Append("%,ld connection%s active<br>\r\n", x, x == 1 ? "" : "s");
Append("</table>\r\n");
Append("</footer>\r\n");
appendf(&outbuf, "%,ld connection%s active<br>\r\n", x, x == 1 ? "" : "s");
appends(&outbuf, "</table>\r\n");
appends(&outbuf, "</footer>\r\n");
p = SetStatus(200, "OK");
p = AppendContentType(p, "text/html");
p = AppendCache(p, 0);
@ -2989,11 +2991,11 @@ static const char *MergeNames(const char *a, const char *b) {
}
static void AppendLong1(const char *a, long x) {
if (x) Append("%s: %ld\r\n", a, x);
if (x) appendf(&outbuf, "%s: %ld\r\n", a, x);
}
static void AppendLong2(const char *a, const char *b, long x) {
if (x) Append("%s.%s: %ld\r\n", a, b, x);
if (x) appendf(&outbuf, "%s.%s: %ld\r\n", a, b, x);
}
static void AppendTimeval(const char *a, struct timeval *tv) {
@ -3687,6 +3689,7 @@ static void LogBody(const char *d, const char *s, size_t n) {
}
static int LuaFetch(lua_State *L) {
#define ssl nope /* TODO(jart): make this file less huge */
char *p;
ssize_t rc;
bool usessl;
@ -3843,7 +3846,7 @@ static int LuaFetch(lua_State *L) {
bio->b = 0;
bio->c = -1;
mbedtls_ssl_set_bio(&sslcli, bio, TlsSend, 0, TlsRecvImpl);
while ((ret = mbedtls_ssl_handshake(&ssl))) {
while ((ret = mbedtls_ssl_handshake(&sslcli))) {
switch (ret) {
case MBEDTLS_ERR_SSL_WANT_READ:
break;
@ -3856,7 +3859,8 @@ static int LuaFetch(lua_State *L) {
}
LockInc(&shared->c.sslhandshakes);
VERBOSEF("SHAKEN %s:%s %s %s", host, port,
mbedtls_ssl_get_ciphersuite(&ssl), mbedtls_ssl_get_version(&ssl));
mbedtls_ssl_get_ciphersuite(&sslcli),
mbedtls_ssl_get_version(&sslcli));
}
/*
@ -4003,13 +4007,13 @@ static int LuaFetch(lua_State *L) {
Finished:
if (paylen && logbodies) LogBody("received", inbuf.p + hdrsize, paylen);
LOGF("FETCH %s HTTP%02d %d %s %`'.*s", method, msg.version, msg.status, urlarg,
HeaderLength(kHttpServer), HeaderData(kHttpServer));
LOGF("FETCH %s HTTP%02d %d %s %`'.*s", method, msg.version, msg.status,
urlarg, HeaderLength(kHttpServer), HeaderData(kHttpServer));
if (followredirect && HasHeader(kHttpLocation) &&
(msg.status == 301 || msg.status == 308 || // permanent redirects
msg.status == 302 || msg.status == 307 || // temporary redirects
msg.status == 303 // see other; non-GET changes to GET, body lost
) && numredirects < maxredirects) {
(msg.status == 301 || msg.status == 308 || // permanent redirects
msg.status == 302 || msg.status == 307 || // temporary redirects
msg.status == 303 /* see other; non-GET changes to GET, body lost */) &&
numredirects < maxredirects) {
// if 303, then remove body and set method to GET
if (msg.status == 303) {
body = "";
@ -4018,8 +4022,8 @@ Finished:
}
// create table if needed
if (!lua_istable(L, 2)) {
lua_settop(L, 1); // pop body if present
lua_createtable(L, 0, 3); // body, method, numredirects
lua_settop(L, 1); // pop body if present
lua_createtable(L, 0, 3); // body, method, numredirects
}
lua_pushlstring(L, body, bodylen);
lua_setfield(L, -2, "body");
@ -4058,6 +4062,7 @@ VerifyFailed:
LuaThrowTlsError(
L, gc(DescribeSslVerifyFailure(sslcli.session_negotiate->verify_result)),
ret);
#undef ssl
}
static int LuaGetDate(lua_State *L) {
@ -4496,7 +4501,7 @@ static int LuaWrite(lua_State *L) {
const char *data;
if (!lua_isnil(L, 1)) {
data = luaL_checklstring(L, 1, &size);
AppendData(data, size);
appendd(&outbuf, data, size);
}
return 0;
}
@ -5173,7 +5178,7 @@ static int LuaRe(lua_State *L) {
return 1;
}
static bool LuaRun(const char *path) {
static bool LuaRun(const char *path, bool mandatory) {
size_t pathlen;
struct Asset *a;
const char *code;
@ -5184,7 +5189,11 @@ static bool LuaRun(const char *path) {
effectivepath.n = pathlen;
DEBUGF("LuaRun(%`'s)", path);
if (luaL_dostring(L, code) != LUA_OK) {
WARNF("%s %s", path, lua_tostring(L, -1));
if (mandatory) {
FATALF("%s %s", path, lua_tostring(L, -1));
} else {
WARNF("%s %s", path, lua_tostring(L, -1));
}
}
free(code);
}
@ -5341,9 +5350,21 @@ static void LuaSetConstant(lua_State *L, const char *s, long x) {
lua_setglobal(L, s);
}
static char *GetDefaultLuaPath(void) {
char *s;
size_t i;
for (s = 0, i = 0; i < stagedirs.n; ++i) {
appendf(&s, "%s/.lua/?.lua;%s/.lua/?/init.lua;", stagedirs.p[i].s,
stagedirs.p[i].s);
}
appends(&s, "zip:.lua/?.lua;zip:.lua/?/init.lua");
return s;
}
static void LuaInit(void) {
#ifndef STATIC
size_t i;
g_lua_path_default = GetDefaultLuaPath();
L = luaL_newstate();
luaL_openlibs(L);
for (i = 0; i < ARRAYLEN(kLuaLibs); ++i) {
@ -5361,7 +5382,7 @@ static void LuaInit(void) {
LuaSetConstant(L, "kLogWarn", kLogWarn);
LuaSetConstant(L, "kLogError", kLogError);
LuaSetConstant(L, "kLogFatal", kLogFatal);
if (LuaRun("/.init.lua")) {
if (LuaRun("/.init.lua", true)) {
hasonhttprequest = IsHookDefined("OnHttpRequest");
hasonclientconnection = IsHookDefined("OnClientConnection");
hasonprocesscreate = IsHookDefined("OnProcessCreate");
@ -5376,7 +5397,7 @@ static void LuaInit(void) {
static void LuaReload(void) {
#ifndef STATIC
if (!LuaRun("/.reload.lua")) {
if (!LuaRun("/.reload.lua", false)) {
DEBUGF("no /.reload.lua defined");
}
#endif
@ -5580,7 +5601,7 @@ static void HandleHeartbeat(void) {
Reindex();
getrusage(RUSAGE_SELF, &shared->server);
#ifndef STATIC
LuaRun("/.heartbeat.lua");
LuaRun("/.heartbeat.lua", false);
CollectGarbage();
#endif
for (i = 0; i < servers.n; ++i) {
@ -6156,13 +6177,14 @@ static bool HandleMessage(void) {
}
static void InitRequest(void) {
assert(!outbuf);
frags = 0;
outbuf = 0;
gzipped = 0;
branded = 0;
content = 0;
msgsize = 0;
loops.n = 0;
outbuf.n = 0;
generator = 0;
luaheaderp = 0;
contentlength = 0;
@ -6614,9 +6636,9 @@ static void MemDestroy(void) {
Free(&unmaplist.p), unmaplist.n = 0;
Free(&freelist.p), freelist.n = 0;
Free(&servers.p), servers.n = 0;
Free(&outbuf.p), outbuf.n = 0;
Free(&hdrbuf.p), hdrbuf.n = 0;
Free(&inbuf.p), inbuf.n = 0;
Free(&outbuf);
FreeStrings(&stagedirs);
FreeStrings(&hidepaths);
Free(&launchbrowser);