diff --git a/libc/fmt/stoa.c b/libc/fmt/stoa.c index d189dffa..6040c1e0 100644 --- a/libc/fmt/stoa.c +++ b/libc/fmt/stoa.c @@ -53,10 +53,8 @@ static int __fmt_stoa_bing(out_f out, void *a, uint64_t w) { static int __fmt_stoa_quoted(out_f out, void *a, uint64_t w) { char buf[8]; - if (w <= 0x7F) { - if (w < 0x20 || w == 0x7F) { - w = cescapec(w); - } + if (isascii(w)) { + w = cescapec(w); } else { w = tpenc(w); } diff --git a/tool/net/demo/.init.lua b/tool/net/demo/.init.lua new file mode 100644 index 00000000..077d60f7 --- /dev/null +++ b/tool/net/demo/.init.lua @@ -0,0 +1,17 @@ +-- /.init.lua is loaded at startup in redbean's main process + +HidePath('/usr/share/zoneinfo/') + +function OnHttpRequest() + if HasParam('magic') then + Write('
\r\n')
+ Write('OnHttpRequest() has intercepted your request
\r\n')
+ Write('because you specified the magic parameter\r\n')
+ Write('
\r\n')
+ Write(EscapeHtml(LoadAsset('/.init.lua')))
+ Write('\r\n')
+ else
+ Route()
+ end
+ SetHeader('Server', 'redbean!')
+end
diff --git a/tool/net/.reload.lua b/tool/net/demo/.reload.lua
similarity index 100%
rename from tool/net/.reload.lua
rename to tool/net/demo/.reload.lua
diff --git a/tool/net/404.html b/tool/net/demo/404.html
similarity index 100%
rename from tool/net/404.html
rename to tool/net/demo/404.html
diff --git a/tool/net/demo/hello.lua b/tool/net/demo/hello.lua
new file mode 100644
index 00000000..60245886
--- /dev/null
+++ b/tool/net/demo/hello.lua
@@ -0,0 +1 @@
+Write('hello world\r\n')
diff --git a/tool/net/index.html b/tool/net/demo/index.html
similarity index 100%
rename from tool/net/index.html
rename to tool/net/demo/index.html
diff --git a/tool/net/redbean-form.lua b/tool/net/demo/redbean-form.lua
similarity index 100%
rename from tool/net/redbean-form.lua
rename to tool/net/demo/redbean-form.lua
diff --git a/tool/net/redbean-xhr.lua b/tool/net/demo/redbean-xhr.lua
similarity index 100%
rename from tool/net/redbean-xhr.lua
rename to tool/net/demo/redbean-xhr.lua
diff --git a/tool/net/redbean.css b/tool/net/demo/redbean.css
similarity index 100%
rename from tool/net/redbean.css
rename to tool/net/demo/redbean.css
diff --git a/tool/net/redbean.lua b/tool/net/demo/redbean.lua
similarity index 95%
rename from tool/net/redbean.lua
rename to tool/net/demo/redbean.lua
index 41727d0e..3c9650fb 100644
--- a/tool/net/redbean.lua
+++ b/tool/net/demo/redbean.lua
@@ -33,10 +33,10 @@ local function main()
SetHeader('Expires', FormatHttpDateTime(GetDate()))
SetHeader('Cache-Control', 'no-cache, must-revalidate, max-age=0')
- -- Roundtripping information can make it safer.
- Write('Thank you for visiting ') - Write(EscapeHtml(EncodeUrl(ParseUrl(GetUrl())))) - Write('\r\n') + -- GetUrl() is the resolved Request-URI (TODO: Maybe change API to return a URL object?) + Write('
Thank you for visiting ')
+ Write(GetUrl()) -- redbean encoded this value so it doesn't need html entity escaping
+ Write('\r\n')
-- GetParam(NAME) is the fastest easiest way to get URL and FORM params
-- If you want the RequestURL query params specifically in full do this
@@ -55,6 +55,12 @@ local function main()
end
end
Write('\r\n')
+ Write("
Whatever you do, don't click on ") + Write('') + Write(EscapeHtml(VisualizeControlCodes(GetPath()))) + Write('?magic\r\n') else Write('
\r\n')
Write('none
\r\n')
diff --git a/tool/net/seekable.txt b/tool/net/demo/seekable.txt
similarity index 100%
rename from tool/net/seekable.txt
rename to tool/net/demo/seekable.txt
diff --git a/tool/net/net.mk b/tool/net/net.mk
index 46a53a7e..5fafc16b 100644
--- a/tool/net/net.mk
+++ b/tool/net/net.mk
@@ -72,13 +72,12 @@ o/$(MODE)/tool/net/redbean.com.dbg: \
o/$(MODE)/tool/net/redbean.com: \
o/$(MODE)/tool/net/redbean.com.dbg \
tool/net/net.mk \
- tool/net/favicon.ico \
- tool/net/redbean.png \
tool/net/.init.lua \
- tool/net/.reload.lua
+ tool/net/favicon.ico \
+ tool/net/redbean.png
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null
- @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/.init.lua tool/net/.reload.lua tool/net/favicon.ico tool/net/redbean.png
+ @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/.init.lua tool/net/favicon.ico tool/net/redbean.png
o/$(MODE)/tool/net/redbean-demo.com.dbg: \
o/$(MODE)/tool/net/redbean.com.dbg
@@ -87,17 +86,18 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
o/$(MODE)/tool/net/redbean-demo.com: \
o/$(MODE)/tool/net/redbean-demo.com.dbg \
tool/net/net.mk \
- tool/net/.init.lua \
- tool/net/.reload.lua \
- tool/net/404.html \
tool/net/favicon.ico \
tool/net/redbean.png \
- tool/net/index.html \
- tool/net/redbean.css \
- tool/net/redbean.lua \
- tool/net/redbean-form.lua \
- tool/net/redbean-xhr.lua \
- tool/net/seekable.txt \
+ tool/net/demo/.init.lua \
+ tool/net/demo/.reload.lua \
+ tool/net/demo/404.html \
+ tool/net/demo/hello.lua \
+ tool/net/demo/index.html \
+ tool/net/demo/redbean.css \
+ tool/net/demo/redbean.lua \
+ tool/net/demo/redbean-form.lua \
+ tool/net/demo/redbean-xhr.lua \
+ tool/net/demo/seekable.txt \
tool/net/redbean.c \
net/http/parsehttprequest.c \
net/http/parseurl.c \
@@ -105,18 +105,20 @@ o/$(MODE)/tool/net/redbean-demo.com: \
test/net/http/parsehttprequest_test.c \
test/net/http/parseurl_test.c
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
- @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null
- @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/.init.lua tool/net/.reload.lua tool/net/redbean.lua tool/net/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/redbean-form.lua tool/net/redbean-xhr.lua
- @$(COMPILE) -AZIP -T$@ zip -qj0 $@ tool/net/seekable.txt
- @$(COMPILE) -AZIP -T$@ zip -q $@ tool/net tool/net/index.html tool/net/redbean.css tool/net/redbean.c net/http/parsehttprequest.c net/http/parseurl.c net/http/encodeurl.c test/net/http/parsehttprequest_test.c test/net/http/parseurl_test.c
+ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-demo
+ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-demo/.ape bs=64 count=11 conv=notrunc 2>/dev/null
+ @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.redbean-demo/.ape tool/net/demo/.init.lua tool/net/demo/.reload.lua tool/net/demo/hello.lua tool/net/demo/redbean.lua 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
+ @$(COMPILE) -AZIP -T$@ zip -qj0 $@ tool/net/demo/seekable.txt
+ @$(COMPILE) -AZIP -T$@ zip -q $@ tool/net/ tool/net/demo/ tool/net/demo/index.html tool/net/demo/redbean.css tool/net/redbean.c net/http/parsehttprequest.c net/http/parseurl.c net/http/encodeurl.c test/net/http/parsehttprequest_test.c test/net/http/parseurl_test.c
o/$(MODE)/tool/net/redbean-static.com: \
o/$(MODE)/tool/net/redbean-static.com.dbg \
tool/net/favicon.ico \
tool/net/redbean.png
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
- @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null
- @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/favicon.ico tool/net/redbean.png
+ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-static
+ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-static/.ape bs=64 count=11 conv=notrunc 2>/dev/null
+ @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.redbean-static/.ape tool/net/favicon.ico tool/net/redbean.png
o/$(MODE)/tool/net/redbean-static.com.dbg: \
$(TOOL_NET_DEPS) \
diff --git a/tool/net/redbean.c b/tool/net/redbean.c
index 34dc1fa7..c7ebcc6a 100644
--- a/tool/net/redbean.c
+++ b/tool/net/redbean.c
@@ -350,12 +350,12 @@ static bool connectionclose;
static bool keyboardinterrupt;
static bool encouragekeepalive;
static bool loggednetworkorigin;
+static bool hasluaglobalhandler;
static int frags;
static int gmtoff;
static int server;
static int client;
-static int warmups;
static int daemonuid;
static int daemongid;
static int statuscode;
@@ -375,6 +375,7 @@ static char *luaheaderp;
static const char *brand;
static const char *pidpath;
static const char *logpath;
+static struct Strings loops;
static size_t contentlength;
static int64_t cacheseconds;
static uint8_t gzip_footer[8];
@@ -954,6 +955,16 @@ static void AddString(struct Strings *l, char *s) {
l->p[l->n - 1] = s;
}
+static bool HasString(struct Strings *l, char *s) {
+ size_t i;
+ for (i = 0; i < l->n; ++i) {
+ if (!strcmp(l->p[i], s)) {
+ return true;
+ }
+ }
+ return false;
+}
+
static void AddStagingDirectory(const char *dirpath) {
char *s;
s = RemoveTrailingSlashes(strdup(dirpath));
@@ -1977,32 +1988,575 @@ static void LaunchBrowser() {
system(openbrowsercommand);
}
+static char *BadMethod(void) {
+ LockInc(&shared->badmethods);
+ return stpcpy(ServeError(405, "Method Not Allowed"), "Allow: GET, HEAD\r\n");
+}
+
+static int GetDecimalWidth(int x) {
+ int w = x ? ceil(log10(x)) : 1;
+ return w + (w - 1) / 3;
+}
+
+static int GetOctalWidth(int x) {
+ return !x ? 1 : x < 8 ? 2 : 1 + bsr(x) / 3;
+}
+
+static const char *DescribeCompressionRatio(char rb[8], uint64_t lf) {
+ long percent;
+ if (ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf) == kZipCompressionNone) {
+ return "n/a";
+ } else {
+ percent = lround(100 - (double)GetZipLfileCompressedSize(zmap + lf) /
+ GetZipLfileUncompressedSize(zmap + lf) * 100);
+ sprintf(rb, "%ld%%", MIN(999, MAX(-999, percent)));
+ return rb;
+ }
+}
+
+static char *ServeListing(void) {
+ long x;
+ ldiv_t y;
+ int w[3];
+ struct tm tm;
+ const char *and;
+ int64_t lastmod;
+ uint64_t cf, lf;
+ struct rusage ru;
+ char *p, *q, *path;
+ char rb[8], tb[64], *rp[6];
+ size_t i, n, pathlen, rn[6];
+ LockInc(&shared->listingrequests);
+ if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod();
+ Append("\
+\r\n\
+\r\n\
+
\r\n");
+ memset(w, 0, sizeof(w));
+ n = GetZipCdirRecords(cdir);
+ for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
+ CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
+ lf = GetZipCfileOffset(zmap + cf);
+ path = GetAssetPath(cf, &pathlen);
+ if (!IsHiddenPath(path)) {
+ w[0] = min(80, max(w[0], strwidth(path, 0) + 2));
+ w[1] = max(w[1], GetOctalWidth(GetZipCfileMode(zmap + cf)));
+ w[2] = max(w[2], GetDecimalWidth(GetZipLfileUncompressedSize(zmap + lf)));
+ }
+ free(path);
+ }
+ n = GetZipCdirRecords(cdir);
+ for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
+ CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
+ lf = GetZipCfileOffset(zmap + cf);
+ path = GetAssetPath(cf, &pathlen);
+ if (!IsHiddenPath(path)) {
+ rp[0] = VisualizeControlCodes(path, pathlen, &rn[0]);
+ rp[1] = EscapePath(path, pathlen, &rn[1]);
+ rp[2] = EscapeHtml(rp[1], rn[1], &rn[2]);
+ rp[3] = VisualizeControlCodes(ZIP_CFILE_COMMENT(zmap + cf),
+ strnlen(ZIP_CFILE_COMMENT(zmap + cf),
+ ZIP_CFILE_COMMENTSIZE(zmap + cf)),
+ &rn[3]);
+ rp[4] = EscapeHtml(rp[0], rn[0], &rn[4]);
+ rp[5] = EscapeHtml(rp[3], rn[3], &rn[0]);
+ lastmod = GetZipCfileLastModified(zmap + cf);
+ localtime_r(&lastmod, &tm);
+ strftime(tb, sizeof(tb), "%Y-%m-%d %H:%M:%S %Z", &tm);
+ if (IsCompressionMethodSupported(
+ ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf)) &&
+ IsAcceptablePath(path, pathlen)) {
+ Append("%-*.*s %s %0*o %4s %,*ld %'s\r\n",
+ rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1],
+ GetZipCfileMode(zmap + cf), DescribeCompressionRatio(rb, lf),
+ w[2], GetZipLfileUncompressedSize(zmap + lf), rp[5]);
+ } else {
+ Append("%-*.*s %s %0*o %4s %,*ld %'s\r\n", w[0], rn[4], rp[4], tb,
+ w[1], GetZipCfileMode(zmap + cf),
+ DescribeCompressionRatio(rb, lf), w[2],
+ GetZipLfileUncompressedSize(zmap + lf), rp[5]);
+ }
+ free(rp[5]);
+ free(rp[4]);
+ free(rp[3]);
+ free(rp[2]);
+ free(rp[1]);
+ free(rp[0]);
+ }
+ free(path);
+ }
+ Append("\r\n");
+ p = SetStatus(200, "OK");
+ p = AppendContentType(p, "text/html");
+ p = AppendCache(p, 0);
+ return CommitOutput(p);
+}
+
+static const char *MergeNames(const char *a, const char *b) {
+ return FreeLater(xasprintf("%s.ru_utime", a));
+}
+
+static void AppendLong1(const char *a, long x) {
+ if (x) Append("%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);
+}
+
+static void AppendTimeval(const char *a, struct timeval *tv) {
+ AppendLong2(a, "tv_sec", tv->tv_sec);
+ AppendLong2(a, "tv_usec", tv->tv_usec);
+}
+
+static void AppendRusage(const char *a, struct rusage *ru) {
+ AppendTimeval(MergeNames(a, "ru_utime"), &ru->ru_utime);
+ AppendTimeval(MergeNames(a, "ru_stime"), &ru->ru_stime);
+ AppendLong2(a, "ru_maxrss", ru->ru_maxrss);
+ AppendLong2(a, "ru_ixrss", ru->ru_ixrss);
+ AppendLong2(a, "ru_idrss", ru->ru_idrss);
+ AppendLong2(a, "ru_isrss", ru->ru_isrss);
+ AppendLong2(a, "ru_minflt", ru->ru_minflt);
+ AppendLong2(a, "ru_majflt", ru->ru_majflt);
+ AppendLong2(a, "ru_nswap", ru->ru_nswap);
+ AppendLong2(a, "ru_inblock", ru->ru_inblock);
+ AppendLong2(a, "ru_oublock", ru->ru_oublock);
+ AppendLong2(a, "ru_msgsnd", ru->ru_msgsnd);
+ AppendLong2(a, "ru_msgrcv", ru->ru_msgrcv);
+ AppendLong2(a, "ru_nsignals", ru->ru_nsignals);
+ AppendLong2(a, "ru_nvcsw", ru->ru_nvcsw);
+ AppendLong2(a, "ru_nivcsw", ru->ru_nivcsw);
+}
+
+static char *ServeStatusz(void) {
+ char *p;
+ LockInc(&shared->statuszrequests);
+ if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod();
+ AppendLong1("pid", getpid());
+ AppendLong1("ppid", getppid());
+ AppendLong1("now", nowl());
+ AppendLong1("nowish", shared->nowish);
+ AppendLong1("heartless", heartless);
+ AppendLong1("gmtoff", gmtoff);
+ AppendLong1("CLK_TCK", CLK_TCK);
+ AppendLong1("startserver", startserver);
+ AppendLong1("lastmeltdown", shared->lastmeltdown);
+ AppendLong1("workers", shared->workers);
+ AppendLong1("assets.n", assets.n);
+ AppendLong1("accepterrors", shared->accepterrors);
+ AppendLong1("acceptinterrupts", shared->acceptinterrupts);
+ AppendLong1("acceptresets", shared->acceptresets);
+ AppendLong1("badlengths", shared->badlengths);
+ AppendLong1("badmessages", shared->badmessages);
+ AppendLong1("badmethods", shared->badmethods);
+ AppendLong1("badranges", shared->badranges);
+ AppendLong1("closeerrors", shared->closeerrors);
+ AppendLong1("compressedresponses", shared->compressedresponses);
+ AppendLong1("connectionshandled", shared->connectionshandled);
+ AppendLong1("connectsrefused", shared->connectsrefused);
+ AppendLong1("continues", shared->continues);
+ AppendLong1("decompressedresponses", shared->decompressedresponses);
+ AppendLong1("deflates", shared->deflates);
+ AppendLong1("dropped", shared->dropped);
+ AppendLong1("dynamicrequests", shared->dynamicrequests);
+ AppendLong1("emfiles", shared->emfiles);
+ AppendLong1("enetdowns", shared->enetdowns);
+ AppendLong1("enfiles", shared->enfiles);
+ AppendLong1("enobufs", shared->enobufs);
+ AppendLong1("enomems", shared->enomems);
+ AppendLong1("enonets", shared->enonets);
+ AppendLong1("errors", shared->errors);
+ AppendLong1("expectsrefused", shared->expectsrefused);
+ AppendLong1("failedchildren", shared->failedchildren);
+ AppendLong1("forbiddens", shared->forbiddens);
+ AppendLong1("forkerrors", shared->forkerrors);
+ AppendLong1("frags", shared->frags);
+ AppendLong1("fumbles", shared->fumbles);
+ AppendLong1("http09", shared->http09);
+ AppendLong1("http10", shared->http10);
+ AppendLong1("http11", shared->http11);
+ AppendLong1("http12", shared->http12);
+ AppendLong1("hugepayloads", shared->hugepayloads);
+ AppendLong1("identityresponses", shared->identityresponses);
+ AppendLong1("inflates", shared->inflates);
+ AppendLong1("listingrequests", shared->listingrequests);
+ AppendLong1("loops", shared->loops);
+ AppendLong1("mapfails", shared->mapfails);
+ AppendLong1("maps", shared->maps);
+ AppendLong1("meltdowns", shared->meltdowns);
+ AppendLong1("messageshandled", shared->messageshandled);
+ AppendLong1("missinglengths", shared->missinglengths);
+ AppendLong1("netafrinic", shared->netafrinic);
+ AppendLong1("netanonymous", shared->netanonymous);
+ AppendLong1("netapnic", shared->netapnic);
+ AppendLong1("netapple", shared->netapple);
+ AppendLong1("netarin", shared->netarin);
+ AppendLong1("netatt", shared->netatt);
+ AppendLong1("netcogent", shared->netcogent);
+ AppendLong1("netcomcast", shared->netcomcast);
+ AppendLong1("netdod", shared->netdod);
+ AppendLong1("netford", shared->netford);
+ AppendLong1("netlacnic", shared->netlacnic);
+ AppendLong1("netloopback", shared->netloopback);
+ AppendLong1("netother", shared->netother);
+ AppendLong1("netprivate", shared->netprivate);
+ AppendLong1("netprudential", shared->netprudential);
+ AppendLong1("netripe", shared->netripe);
+ AppendLong1("nettestnet", shared->nettestnet);
+ AppendLong1("netusps", shared->netusps);
+ AppendLong1("notfounds", shared->notfounds);
+ AppendLong1("notmodifieds", shared->notmodifieds);
+ AppendLong1("openfails", shared->openfails);
+ AppendLong1("partialresponses", shared->partialresponses);
+ AppendLong1("payloaddisconnects", shared->payloaddisconnects);
+ AppendLong1("pipelinedrequests", shared->pipelinedrequests);
+ AppendLong1("precompressedresponses", shared->precompressedresponses);
+ AppendLong1("readerrors", shared->readerrors);
+ AppendLong1("readinterrupts", shared->readinterrupts);
+ AppendLong1("readresets", shared->readresets);
+ AppendLong1("readtimeouts", shared->readtimeouts);
+ AppendLong1("redirects", shared->redirects);
+ AppendLong1("reloads", shared->reloads);
+ AppendLong1("rewrites", shared->rewrites);
+ AppendLong1("serveroptions", shared->serveroptions);
+ AppendLong1("shutdowns", shared->shutdowns);
+ AppendLong1("slowloris", shared->slowloris);
+ AppendLong1("slurps", shared->slurps);
+ AppendLong1("statfails", shared->statfails);
+ AppendLong1("staticrequests", shared->staticrequests);
+ AppendLong1("stats", shared->stats);
+ AppendLong1("statuszrequests", shared->statuszrequests);
+ AppendLong1("synchronizationfailures", shared->synchronizationfailures);
+ AppendLong1("terminatedchildren", shared->terminatedchildren);
+ AppendLong1("thiscorruption", shared->thiscorruption);
+ AppendLong1("transfersrefused", shared->transfersrefused);
+ AppendLong1("urisrefused", shared->urisrefused);
+ AppendLong1("verifies", shared->verifies);
+ AppendLong1("writeerrors", shared->writeerrors);
+ AppendLong1("writeinterruputs", shared->writeinterruputs);
+ AppendLong1("writeresets", shared->writeresets);
+ AppendLong1("writetimeouts", shared->writetimeouts);
+ AppendRusage("server", &shared->server);
+ AppendRusage("children", &shared->children);
+ p = SetStatus(200, "OK");
+ p = AppendContentType(p, "text/plain");
+ p = AppendCache(p, 0);
+ return CommitOutput(p);
+}
+
+static char *RedirectSlash(void) {
+ size_t n;
+ char *p, *e;
+ LockInc(&shared->redirects);
+ p = SetStatus(307, "Temporary Redirect");
+ p = stpcpy(p, "Location: ");
+ e = EscapePath(url.path.p, url.path.n, &n);
+ p = mempcpy(p, e, n);
+ p = stpcpy(p, "/\r\n");
+ free(e);
+ return p;
+}
+
+static char *RoutePath(const char *, size_t);
+static char *ServeIndex(const char *path, size_t pathlen) {
+ size_t i, n;
+ char *p, *q;
+ p = NULL;
+ for (i = 0; !p && i < ARRAYLEN(kIndexPaths); ++i) {
+ q = MergePaths(path, pathlen, kIndexPaths[i], strlen(kIndexPaths[i]), &n);
+ p = RoutePath(q, n);
+ free(q);
+ }
+ return p;
+}
+
+static bool IsLua(struct Asset *a) {
+ if (a->file) return endswith(a->file->path, ".lua");
+ return ZIP_LFILE_NAMESIZE(zmap + a->lf) >= 4 &&
+ !memcmp(ZIP_LFILE_NAME(zmap + a->lf) +
+ ZIP_LFILE_NAMESIZE(zmap + a->lf) - 4,
+ ".lua", 4);
+}
+
+static char *GetLuaResponse(void) {
+ char *p;
+ if (!(p = luaheaderp)) {
+ p = SetStatus(200, "OK");
+ p = AppendContentType(p, "text/html");
+ }
+ return p;
+}
+
+static char *ServeLua(struct Asset *a, const char *path, size_t pathlen) {
+ char *code;
+ effectivepath.p = path;
+ effectivepath.n = pathlen;
+ if ((code = FreeLater(LoadAsset(a, NULL)))) {
+ if (luaL_dostring(L, code) == LUA_OK) {
+ return CommitOutput(GetLuaResponse());
+ } else {
+ WARNF("%s", lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+ }
+ return ServeError(500, "Internal Server Error");
+}
+
+static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) {
+#ifndef STATIC
+ if (IsLua(a)) {
+ LockInc(&shared->dynamicrequests);
+ return ServeLua(a, path, pathlen);
+ }
+#endif
+ if (msg.method == kHttpGet || msg.method == kHttpHead) {
+ LockInc(&shared->staticrequests);
+ return stpcpy(ServeAsset(a, path, pathlen),
+ "X-Content-Type-Options: nosniff\r\n");
+ } else {
+ return BadMethod();
+ }
+}
+
+static char *HandleRedirect(struct Redirect *r) {
+ int code;
+ struct Asset *a;
+ if (!r->code && (a = GetAsset(r->location, strlen(r->location)))) {
+ LockInc(&shared->rewrites);
+ DEBUGF("rewriting to %`'s", r->location);
+ if (!HasString(&loops, r->location)) {
+ AddString(&loops, r->location);
+ return RoutePath(r->location, strlen(r->location));
+ } else {
+ LockInc(&shared->loops);
+ return SetStatus(508, "Loop Detected");
+ }
+ } else if (msg.version < 10) {
+ return ServeError(505, "HTTP Version Not Supported");
+ } else {
+ LockInc(&shared->redirects);
+ code = r->code;
+ if (!code) code = 307;
+ DEBUGF("%d redirecting %`'s", code, r->location);
+ return AppendHeader(SetStatus(code, GetHttpReason(code)), "Location",
+ FreeLater(EncodeHttpHeaderValue(r->location, -1, 0)));
+ }
+}
+
+static char *HandleFolder(const char *path, size_t pathlen) {
+ char *p;
+ if (url.path.n && url.path.p[url.path.n - 1] != '/' &&
+ SlicesEqual(path, pathlen, url.path.p, url.path.n)) {
+ return RedirectSlash();
+ }
+ if ((p = ServeIndex(path, pathlen))) {
+ return p;
+ } else {
+ LockInc(&shared->forbiddens);
+ LOGF("directory %`'.*s lacks index page", pathlen, path);
+ return ServeError(403, "Forbidden");
+ }
+}
+
+static char *RoutePath(const char *path, size_t pathlen) {
+ int m;
+ long r;
+ char *p;
+ struct Asset *a;
+ DEBUGF("RoutePath(%`'.*s)", pathlen, path);
+ if ((a = GetAsset(path, pathlen))) {
+ if ((m = GetMode(a)) & 0004) {
+ if (!S_ISDIR(m)) {
+ return HandleAsset(a, path, pathlen);
+ } else {
+ return HandleFolder(path, pathlen);
+ }
+ } else {
+ LockInc(&shared->forbiddens);
+ LOGF("asset %`'.*s %#o isn't readable", pathlen, path, m);
+ return ServeError(403, "Forbidden");
+ }
+ } else if ((r = FindRedirect(path, pathlen)) != -1) {
+ return HandleRedirect(redirects.p + r);
+ } else {
+ return NULL;
+ }
+}
+
+static char *RouteHost(const char *host, size_t hostlen, const char *path,
+ size_t pathlen) {
+ size_t hn;
+ char *hp, *p;
+ hn = 1 + hostlen + url.path.n;
+ hp = FreeLater(xmalloc(3 + 1 + hn));
+ hp[0] = '/';
+ mempcpy(mempcpy(hp + 1, host, hostlen), path, pathlen);
+ if ((p = RoutePath(hp, hn))) return p;
+ if (ParseIp(host, hostlen) == -1) {
+ if (hostlen > 4 && !memcmp(host, "www.", 4)) {
+ mempcpy(mempcpy(hp + 1, host + 4, hostlen - 4), path, pathlen);
+ if ((p = RoutePath(hp, hn - 4))) return p;
+ } else {
+ mempcpy(mempcpy(mempcpy(hp + 1, "www.", 4), host, hostlen), path,
+ pathlen);
+ if ((p = RoutePath(hp, hn + 4))) return p;
+ }
+ }
+ return NULL;
+}
+
+static char *Route(const char *host, size_t hostlen, const char *path,
+ size_t pathlen) {
+ char *p;
+ if (hostlen && (p = RouteHost(host, hostlen, path, pathlen))) {
+ return p;
+ }
+ if (SlicesEqual(path, pathlen, "/", 1)) {
+ if ((p = ServeIndex("/", 1))) return p;
+ return ServeListing();
+ } else if ((p = RoutePath(path, pathlen))) {
+ return p;
+ } else if (SlicesEqual(path, pathlen, "/statusz", 8)) {
+ return ServeStatusz();
+ } else {
+ LockInc(&shared->notfounds);
+ return ServeError(404, "Not Found");
+ }
+}
+
static const char *LuaCheckPath(lua_State *L, int idx, size_t *pathlen) {
const char *path;
- path = luaL_checklstring(L, idx, pathlen);
- if (!IsReasonablePath(path, *pathlen)) {
- WARNF("bad path %`'.*s", *pathlen, path);
- luaL_argerror(L, idx, "bad path");
- unreachable;
+ if (lua_isnoneornil(L, idx)) {
+ path = url.path.p;
+ *pathlen = url.path.n;
+ } else {
+ path = luaL_checklstring(L, idx, pathlen);
+ if (!IsReasonablePath(path, *pathlen)) {
+ WARNF("bad path %`'.*s", *pathlen, path);
+ luaL_argerror(L, idx, "bad path");
+ unreachable;
+ }
}
return path;
}
+static const char *LuaCheckHost(lua_State *L, int idx, size_t *hostlen) {
+ const char *host;
+ if (lua_isnoneornil(L, idx)) {
+ host = url.host.p;
+ *hostlen = url.host.n;
+ } else {
+ host = luaL_checklstring(L, idx, hostlen);
+ if (!IsAcceptableHost(host, *hostlen)) {
+ WARNF("bad host %`'.*s", *hostlen, host);
+ luaL_argerror(L, idx, "bad host");
+ unreachable;
+ }
+ }
+ return host;
+}
+
+static int LuaServeListing(lua_State *L) {
+ luaheaderp = ServeListing();
+ return 0;
+}
+
+static int LuaServeStatusz(lua_State *L) {
+ luaheaderp = ServeStatusz();
+ return 0;
+}
+
static int LuaServeAsset(lua_State *L) {
size_t pathlen;
struct Asset *a;
const char *path;
path = LuaCheckPath(L, 1, &pathlen);
- if (!(a = GetAsset(path, pathlen))) {
- luaL_argerror(L, 1, "not found");
- unreachable;
+ if ((a = GetAsset(path, pathlen)) && !S_ISDIR(GetMode(a))) {
+ luaheaderp = ServeAsset(a, path, pathlen);
+ lua_pushboolean(L, true);
+ } else {
+ lua_pushboolean(L, false);
}
- if (S_ISDIR(GetMode(a))) {
- luaL_argerror(L, 1, "is a directory");
- unreachable;
- }
- luaheaderp = ServeAsset(a, path, pathlen);
- return 0;
+ return 1;
+}
+
+static int LuaServeIndex(lua_State *L) {
+ size_t pathlen;
+ const char *path;
+ path = LuaCheckPath(L, 1, &pathlen);
+ lua_pushboolean(L, !!(luaheaderp = ServeIndex(path, pathlen)));
+ return 1;
+}
+
+static int LuaRoutePath(lua_State *L) {
+ size_t pathlen;
+ const char *path;
+ path = LuaCheckPath(L, 1, &pathlen);
+ lua_pushboolean(L, !!(luaheaderp = RoutePath(path, pathlen)));
+ return 1;
+}
+
+static int LuaRouteHost(lua_State *L) {
+ size_t hostlen, pathlen;
+ const char *host, *path;
+ host = LuaCheckHost(L, 1, &hostlen);
+ path = LuaCheckPath(L, 2, &pathlen);
+ lua_pushboolean(L, !!(luaheaderp = RouteHost(host, hostlen, path, pathlen)));
+ return 1;
+}
+
+static int LuaRoute(lua_State *L) {
+ size_t hostlen, pathlen;
+ const char *host, *path;
+ host = LuaCheckHost(L, 1, &hostlen);
+ path = LuaCheckPath(L, 2, &pathlen);
+ lua_pushboolean(L, !!(luaheaderp = Route(host, hostlen, path, pathlen)));
+ return 1;
}
static int LuaRespond(lua_State *L, char *respond(unsigned, const char *)) {
@@ -2141,16 +2695,12 @@ static int LuaCategorizeIp(lua_State *L) {
return 1;
}
-static void LuaPushLatin1(lua_State *L, const char *s, size_t n) {
- char *t;
- size_t m;
- t = DecodeLatin1(s, n, &m);
- lua_pushlstring(L, t, m);
- free(t);
-}
-
static int LuaGetUrl(lua_State *L) {
- LuaPushLatin1(L, inbuf.p + msg.uri.a, msg.uri.b - msg.uri.a);
+ char *p;
+ size_t n;
+ p = EncodeUrl(&url, &n);
+ lua_pushlstring(L, p, n);
+ free(p);
return 1;
}
@@ -2253,6 +2803,14 @@ static int LuaGetPayload(lua_State *L) {
return 1;
}
+static void LuaPushLatin1(lua_State *L, const char *s, size_t n) {
+ char *t;
+ size_t m;
+ t = DecodeLatin1(s, n, &m);
+ lua_pushlstring(L, t, m);
+ free(t);
+}
+
static char *FoldHeader(int h, size_t *z) {
char *p;
size_t i, n, m;
@@ -2330,8 +2888,8 @@ static int LuaGetHeaders(lua_State *L) {
static int LuaSetHeader(lua_State *L) {
int h;
- char *p;
ssize_t rc;
+ char *p, *q;
const char *key, *val, *eval;
size_t i, keylen, vallen, evallen;
key = luaL_checklstring(L, 1, &keylen);
@@ -2346,16 +2904,12 @@ static int LuaSetHeader(lua_State *L) {
luaL_argerror(L, 2, "invalid");
unreachable;
}
- if (!luaheaderp) {
- p = SetStatus(200, "OK");
- } else {
- while (luaheaderp - hdrbuf.p + keylen + 2 + evallen + 2 + 512 > hdrbuf.n) {
- hdrbuf.n += hdrbuf.n >> 1;
- p = xrealloc(hdrbuf.p, hdrbuf.n);
- luaheaderp = p + (luaheaderp - hdrbuf.p);
- hdrbuf.p = p;
- }
- p = luaheaderp;
+ p = GetLuaResponse();
+ while (p - hdrbuf.p + keylen + 2 + evallen + 2 + 512 > hdrbuf.n) {
+ hdrbuf.n += hdrbuf.n >> 1;
+ q = xrealloc(hdrbuf.p, hdrbuf.n);
+ luaheaderp = p = q + (p - hdrbuf.p);
+ hdrbuf.p = q;
}
switch (h) {
case kHttpConnection:
@@ -2590,6 +3144,10 @@ static int LuaIsAcceptablePath(lua_State *L) {
return LuaIsValid(L, IsAcceptablePath);
}
+static int LuaIsReasonablePath(lua_State *L) {
+ return LuaIsValid(L, IsReasonablePath);
+}
+
static int LuaIsAcceptableHost(lua_State *L) {
return LuaIsValid(L, IsAcceptableHost);
}
@@ -3156,6 +3714,7 @@ static const luaL_Reg kLuaFuncs[] = {
{"IsLoopbackIp", LuaIsLoopbackIp}, //
{"IsPrivateIp", LuaIsPrivateIp}, //
{"IsPublicIp", LuaIsPublicIp}, //
+ {"IsReasonablePath", LuaIsReasonablePath}, //
{"IsValidHttpToken", LuaIsValidHttpToken}, //
{"LaunchBrowser", LuaLaunchBrowser}, //
{"LoadAsset", LuaLoadAsset}, //
@@ -3172,8 +3731,14 @@ static const luaL_Reg kLuaFuncs[] = {
{"ProgramPort", LuaProgramPort}, //
{"ProgramRedirect", LuaProgramRedirect}, //
{"ProgramTimeout", LuaProgramTimeout}, //
+ {"Route", LuaRoute}, //
+ {"RouteHost", LuaRouteHost}, //
+ {"RoutePath", LuaRoutePath}, //
{"ServeAsset", LuaServeAsset}, //
{"ServeError", LuaServeError}, //
+ {"ServeIndex", LuaServeIndex}, //
+ {"ServeListing", LuaServeListing}, //
+ {"ServeStatusz", LuaServeStatusz}, //
{"SetHeader", LuaSetHeader}, //
{"SetLogLevel", LuaSetLogLevel}, //
{"SetStatus", LuaSetStatus}, //
@@ -3226,7 +3791,10 @@ static void LuaInit(void) {
LuaSetConstant(L, "kLogWarn", kLogWarn);
LuaSetConstant(L, "kLogError", kLogError);
LuaSetConstant(L, "kLogFatal", kLogFatal);
- if (!LuaRun("/.init.lua")) {
+ if (LuaRun("/.init.lua")) {
+ hasluaglobalhandler = !!lua_getglobal(L, "OnHttpRequest");
+ lua_pop(L, 1);
+ } else {
DEBUGF("no /.init.lua defined");
}
#endif
@@ -3254,75 +3822,6 @@ static void HandleHeartbeat(void) {
#endif
}
-static char *ServeLua(struct Asset *a, const char *path, size_t pathlen) {
- char *p, *code;
- luaheaderp = NULL;
- effectivepath.p = path;
- effectivepath.n = pathlen;
- if ((code = FreeLater(LoadAsset(a, NULL)))) {
- if (luaL_dostring(L, code) == LUA_OK) {
- if (!(p = luaheaderp)) {
- p = SetStatus(200, "OK");
- p = AppendContentType(p, "text/html");
- }
- return CommitOutput(p);
- } else {
- WARNF("%s %s", DescribeClient(), lua_tostring(L, -1));
- lua_pop(L, 1); /* remove message */
- connectionclose = true;
- }
- }
- return ServeError(500, "Internal Server Error");
-}
-
-static bool IsLua(struct Asset *a) {
- if (a->file) return endswith(a->file->path, ".lua");
- return ZIP_LFILE_NAMESIZE(zmap + a->lf) >= 4 &&
- !memcmp(ZIP_LFILE_NAME(zmap + a->lf) +
- ZIP_LFILE_NAMESIZE(zmap + a->lf) - 4,
- ".lua", 4);
-}
-
-static char *BadMethod(void) {
- LockInc(&shared->badmethods);
- return stpcpy(ServeError(405, "Method Not Allowed"), "Allow: GET, HEAD\r\n");
-}
-
-static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) {
-#ifndef STATIC
- if (IsLua(a)) {
- LockInc(&shared->dynamicrequests);
- return ServeLua(a, path, pathlen);
- }
-#endif
- if (msg.method == kHttpGet || msg.method == kHttpHead) {
- LockInc(&shared->staticrequests);
- return stpcpy(ServeAsset(a, path, pathlen),
- "X-Content-Type-Options: nosniff\r\n");
- } else {
- return BadMethod();
- }
-}
-
-static char *HandleRedirect(struct Redirect *r) {
- int code;
- struct Asset *a;
- if (!r->code && (a = GetAsset(r->location, strlen(r->location)))) {
- LockInc(&shared->rewrites);
- DEBUGF("rewriting to %`'s", r->location);
- return HandleAsset(a, r->location, strlen(r->location));
- } else if (msg.version < 10) {
- return ServeError(505, "HTTP Version Not Supported");
- } else {
- LockInc(&shared->redirects);
- code = r->code;
- if (!code) code = 307;
- DEBUGF("%d redirecting %`'s", code, r->location);
- return AppendHeader(SetStatus(code, GetHttpReason(code)), "Location",
- FreeLater(EncodeHttpHeaderValue(r->location, -1, 0)));
- }
-}
-
static void LogMessage(const char *d, const char *s, size_t n) {
size_t n2, n3;
char *s2, *s3;
@@ -3384,41 +3883,6 @@ Content-Length: 0\r\n\
\r\n");
}
-static void SendWarmupRequests(void) {
- int pid, i;
- struct sockaddr_in addr;
- static const char kWarmup[] = "\
-OPTIONS * HTTP/1.1\n\
-User-Agent: redbean/0.4\n\
-\n\
-GET /statusz HTTP/1.1\n\
-User-Agent: redbean/0.4\n\
-\n";
- if (uniprocess) return;
- switch (fork()) {
- default:
- warmups = 2;
- ++shared->workers;
- break;
- case 0:
- DEBUGF("sending warmup requests");
- memcpy(&addr, &serveraddr, sizeof(addr));
- if (addr.sin_addr.s_addr == htonl(INADDR_ANY)) {
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- }
- for (i = 0; i < 2; ++i) {
- client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- connect(client, &addr, sizeof(addr));
- write(client, kWarmup, sizeof(kWarmup) - 1);
- read(client, inbuf.p, inbuf.n);
- close(client);
- }
- _exit(0);
- case -1:
- break;
- }
-}
-
static void LogClose(const char *reason) {
if (amtread || meltdown || killed) {
LockInc(&shared->fumbles);
@@ -3438,27 +3902,6 @@ static const char *DescribeClose(void) {
return "destroyed";
}
-static const char *DescribeCompressionRatio(char rb[8], uint64_t lf) {
- long percent;
- if (ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf) == kZipCompressionNone) {
- return "n/a";
- } else {
- percent = lround(100 - (double)GetZipLfileCompressedSize(zmap + lf) /
- GetZipLfileUncompressedSize(zmap + lf) * 100);
- sprintf(rb, "%ld%%", MIN(999, MAX(-999, percent)));
- return rb;
- }
-}
-
-static int GetDecimalWidth(int x) {
- int w = x ? ceil(log10(x)) : 1;
- return w + (w - 1) / 3;
-}
-
-static int GetOctalWidth(int x) {
- return !x ? 1 : x < 8 ? 2 : 1 + bsr(x) / 3;
-}
-
static void RecordNetworkOrigin(void) {
uint32_t ip;
GetRemoteAddr(&ip, 0);
@@ -3520,279 +3963,6 @@ static void RecordNetworkOrigin(void) {
}
}
-static const char *MergeNames(const char *a, const char *b) {
- return FreeLater(xasprintf("%s.ru_utime", a));
-}
-
-static void AppendLong1(const char *a, long x) {
- if (x) Append("%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);
-}
-
-static void AppendTimeval(const char *a, struct timeval *tv) {
- AppendLong2(a, "tv_sec", tv->tv_sec);
- AppendLong2(a, "tv_usec", tv->tv_usec);
-}
-
-static void AppendRusage(const char *a, struct rusage *ru) {
- AppendTimeval(MergeNames(a, "ru_utime"), &ru->ru_utime);
- AppendTimeval(MergeNames(a, "ru_stime"), &ru->ru_stime);
- AppendLong2(a, "ru_maxrss", ru->ru_maxrss);
- AppendLong2(a, "ru_ixrss", ru->ru_ixrss);
- AppendLong2(a, "ru_idrss", ru->ru_idrss);
- AppendLong2(a, "ru_isrss", ru->ru_isrss);
- AppendLong2(a, "ru_minflt", ru->ru_minflt);
- AppendLong2(a, "ru_majflt", ru->ru_majflt);
- AppendLong2(a, "ru_nswap", ru->ru_nswap);
- AppendLong2(a, "ru_inblock", ru->ru_inblock);
- AppendLong2(a, "ru_oublock", ru->ru_oublock);
- AppendLong2(a, "ru_msgsnd", ru->ru_msgsnd);
- AppendLong2(a, "ru_msgrcv", ru->ru_msgrcv);
- AppendLong2(a, "ru_nsignals", ru->ru_nsignals);
- AppendLong2(a, "ru_nvcsw", ru->ru_nvcsw);
- AppendLong2(a, "ru_nivcsw", ru->ru_nivcsw);
-}
-
-char *ServeStatusz(void) {
- char *p;
- if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod();
- AppendLong1("pid", getpid());
- AppendLong1("ppid", getppid());
- AppendLong1("now", nowl());
- AppendLong1("nowish", shared->nowish);
- AppendLong1("heartless", heartless);
- AppendLong1("gmtoff", gmtoff);
- AppendLong1("CLK_TCK", CLK_TCK);
- AppendLong1("startserver", startserver);
- AppendLong1("lastmeltdown", shared->lastmeltdown);
- AppendLong1("workers", shared->workers);
- AppendLong1("assets.n", assets.n);
- AppendLong1("accepterrors", shared->accepterrors);
- AppendLong1("acceptinterrupts", shared->acceptinterrupts);
- AppendLong1("acceptresets", shared->acceptresets);
- AppendLong1("badlengths", shared->badlengths);
- AppendLong1("badmessages", shared->badmessages);
- AppendLong1("badmethods", shared->badmethods);
- AppendLong1("badranges", shared->badranges);
- AppendLong1("closeerrors", shared->closeerrors);
- AppendLong1("compressedresponses", shared->compressedresponses);
- AppendLong1("connectionshandled", shared->connectionshandled);
- AppendLong1("connectsrefused", shared->connectsrefused);
- AppendLong1("continues", shared->continues);
- AppendLong1("decompressedresponses", shared->decompressedresponses);
- AppendLong1("deflates", shared->deflates);
- AppendLong1("dropped", shared->dropped);
- AppendLong1("dynamicrequests", shared->dynamicrequests);
- AppendLong1("emfiles", shared->emfiles);
- AppendLong1("enetdowns", shared->enetdowns);
- AppendLong1("enfiles", shared->enfiles);
- AppendLong1("enobufs", shared->enobufs);
- AppendLong1("enomems", shared->enomems);
- AppendLong1("enonets", shared->enonets);
- AppendLong1("errors", shared->errors);
- AppendLong1("expectsrefused", shared->expectsrefused);
- AppendLong1("failedchildren", shared->failedchildren);
- AppendLong1("forbiddens", shared->forbiddens);
- AppendLong1("forkerrors", shared->forkerrors);
- AppendLong1("frags", shared->frags);
- AppendLong1("fumbles", shared->fumbles);
- AppendLong1("http09", shared->http09);
- AppendLong1("http10", shared->http10);
- AppendLong1("http11", shared->http11);
- AppendLong1("http12", shared->http12);
- AppendLong1("hugepayloads", shared->hugepayloads);
- AppendLong1("identityresponses", shared->identityresponses);
- AppendLong1("inflates", shared->inflates);
- AppendLong1("listingrequests", shared->listingrequests);
- AppendLong1("loops", shared->loops);
- AppendLong1("mapfails", shared->mapfails);
- AppendLong1("maps", shared->maps);
- AppendLong1("meltdowns", shared->meltdowns);
- AppendLong1("messageshandled", shared->messageshandled);
- AppendLong1("missinglengths", shared->missinglengths);
- AppendLong1("netafrinic", shared->netafrinic);
- AppendLong1("netanonymous", shared->netanonymous);
- AppendLong1("netapnic", shared->netapnic);
- AppendLong1("netapple", shared->netapple);
- AppendLong1("netarin", shared->netarin);
- AppendLong1("netatt", shared->netatt);
- AppendLong1("netcogent", shared->netcogent);
- AppendLong1("netcomcast", shared->netcomcast);
- AppendLong1("netdod", shared->netdod);
- AppendLong1("netford", shared->netford);
- AppendLong1("netlacnic", shared->netlacnic);
- AppendLong1("netloopback", shared->netloopback);
- AppendLong1("netother", shared->netother);
- AppendLong1("netprivate", shared->netprivate);
- AppendLong1("netprudential", shared->netprudential);
- AppendLong1("netripe", shared->netripe);
- AppendLong1("nettestnet", shared->nettestnet);
- AppendLong1("netusps", shared->netusps);
- AppendLong1("notfounds", shared->notfounds);
- AppendLong1("notmodifieds", shared->notmodifieds);
- AppendLong1("openfails", shared->openfails);
- AppendLong1("partialresponses", shared->partialresponses);
- AppendLong1("payloaddisconnects", shared->payloaddisconnects);
- AppendLong1("pipelinedrequests", shared->pipelinedrequests);
- AppendLong1("precompressedresponses", shared->precompressedresponses);
- AppendLong1("readerrors", shared->readerrors);
- AppendLong1("readinterrupts", shared->readinterrupts);
- AppendLong1("readresets", shared->readresets);
- AppendLong1("readtimeouts", shared->readtimeouts);
- AppendLong1("redirects", shared->redirects);
- AppendLong1("reloads", shared->reloads);
- AppendLong1("rewrites", shared->rewrites);
- AppendLong1("serveroptions", shared->serveroptions);
- AppendLong1("shutdowns", shared->shutdowns);
- AppendLong1("slowloris", shared->slowloris);
- AppendLong1("slurps", shared->slurps);
- AppendLong1("statfails", shared->statfails);
- AppendLong1("staticrequests", shared->staticrequests);
- AppendLong1("stats", shared->stats);
- AppendLong1("statuszrequests", shared->statuszrequests);
- AppendLong1("synchronizationfailures", shared->synchronizationfailures);
- AppendLong1("terminatedchildren", shared->terminatedchildren);
- AppendLong1("thiscorruption", shared->thiscorruption);
- AppendLong1("transfersrefused", shared->transfersrefused);
- AppendLong1("urisrefused", shared->urisrefused);
- AppendLong1("verifies", shared->verifies);
- AppendLong1("writeerrors", shared->writeerrors);
- AppendLong1("writeinterruputs", shared->writeinterruputs);
- AppendLong1("writeresets", shared->writeresets);
- AppendLong1("writetimeouts", shared->writetimeouts);
- AppendRusage("server", &shared->server);
- AppendRusage("children", &shared->children);
- p = SetStatus(200, "OK");
- p = AppendContentType(p, "text/plain");
- p = AppendCache(p, 0);
- return CommitOutput(p);
-}
-
-char *ServeListing(void) {
- long x;
- ldiv_t y;
- int w[3];
- struct tm tm;
- const char *and;
- int64_t lastmod;
- uint64_t cf, lf;
- struct rusage ru;
- char *p, *q, *path;
- char rb[8], tb[64], *rp[6];
- size_t i, n, pathlen, rn[6];
- if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod();
- Append("\
-\r\n\
-\r\n\
-\r\n");
- memset(w, 0, sizeof(w));
- n = GetZipCdirRecords(cdir);
- for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
- CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
- lf = GetZipCfileOffset(zmap + cf);
- path = GetAssetPath(cf, &pathlen);
- if (!IsHiddenPath(path)) {
- w[0] = min(80, max(w[0], strwidth(path, 0) + 2));
- w[1] = max(w[1], GetOctalWidth(GetZipCfileMode(zmap + cf)));
- w[2] = max(w[2], GetDecimalWidth(GetZipLfileUncompressedSize(zmap + lf)));
- }
- free(path);
- }
- n = GetZipCdirRecords(cdir);
- for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
- CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
- lf = GetZipCfileOffset(zmap + cf);
- path = GetAssetPath(cf, &pathlen);
- if (!IsHiddenPath(path)) {
- rp[0] = VisualizeControlCodes(path, pathlen, &rn[0]);
- rp[1] = EscapePath(path, pathlen, &rn[1]);
- rp[2] = EscapeHtml(rp[1], rn[1], &rn[2]);
- rp[3] = VisualizeControlCodes(ZIP_CFILE_COMMENT(zmap + cf),
- strnlen(ZIP_CFILE_COMMENT(zmap + cf),
- ZIP_CFILE_COMMENTSIZE(zmap + cf)),
- &rn[3]);
- rp[4] = EscapeHtml(rp[0], rn[0], &rn[4]);
- rp[5] = EscapeHtml(rp[3], rn[3], &rn[0]);
- lastmod = GetZipCfileLastModified(zmap + cf);
- localtime_r(&lastmod, &tm);
- strftime(tb, sizeof(tb), "%Y-%m-%d %H:%M:%S %Z", &tm);
- if (IsCompressionMethodSupported(
- ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf)) &&
- IsAcceptablePath(path, pathlen)) {
- Append("%-*.*s %s %0*o %4s %,*ld %'s\r\n",
- rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1],
- GetZipCfileMode(zmap + cf), DescribeCompressionRatio(rb, lf),
- w[2], GetZipLfileUncompressedSize(zmap + lf), rp[5]);
- } else {
- Append("%-*.*s %s %0*o %4s %,*ld %'s\r\n", w[0], rn[4], rp[4], tb,
- w[1], GetZipCfileMode(zmap + cf),
- DescribeCompressionRatio(rb, lf), w[2],
- GetZipLfileUncompressedSize(zmap + lf), rp[5]);
- }
- free(rp[5]);
- free(rp[4]);
- free(rp[3]);
- free(rp[2]);
- free(rp[1]);
- free(rp[0]);
- }
- free(path);
- }
- Append("\r\n");
- p = SetStatus(200, "OK");
- p = AppendContentType(p, "text/html");
- p = AppendCache(p, 0);
- return CommitOutput(p);
-}
-
char *ServeServerOptions(void) {
char *p;
p = SetStatus(200, "OK");
@@ -3805,7 +3975,6 @@ char *ServeServerOptions(void) {
#endif
return p;
}
-
static bool HasAtMostThisElement(int h, const char *s) {
size_t i, n;
struct HttpRequestHeader *x;
@@ -3917,93 +4086,19 @@ static void ParseRequestParameters(void) {
FreeLater(url.params.p);
}
-static char *RedirectSlash(void) {
- size_t n;
- char *p, *e;
- if (url.path.n && url.path.p[url.path.n - 1] != '/') {
- LockInc(&shared->redirects);
- p = SetStatus(307, "Temporary Redirect");
- p = stpcpy(p, "Location: ");
- e = EscapePath(url.path.p, url.path.n, &n);
- p = mempcpy(p, e, n);
- p = stpcpy(p, "/\r\n");
- free(e);
- return p;
+static char *OnHttpRequest(void) {
+ effectivepath.p = url.path.p;
+ effectivepath.n = url.path.n;
+ lua_getglobal(L, "OnHttpRequest");
+ if (lua_pcall(L, 0, 0, 0) == LUA_OK) {
+ return CommitOutput(GetLuaResponse());
} else {
- LockInc(&shared->loops);
- return SetStatus(508, "Loop Detected");
+ WARNF("%s", lua_tostring(L, -1));
+ lua_pop(L, 1);
+ return ServeError(500, "Internal Server Error");
}
}
-static char *TryPath(const char *, size_t);
-static char *TryIndex(const char *path, size_t pathlen) {
- size_t i, n;
- char *p, *q;
- p = NULL;
- for (i = 0; !p && i < ARRAYLEN(kIndexPaths); ++i) {
- q = MergePaths(path, pathlen, kIndexPaths[i], strlen(kIndexPaths[i]), &n);
- p = TryPath(q, n);
- free(q);
- }
- return p;
-}
-
-static char *TryPath(const char *path, size_t pathlen) {
- int m;
- long r;
- char *p;
- struct Asset *a;
- DEBUGF("TryPath(%`'.*s)", pathlen, path);
- if ((a = GetAsset(path, pathlen))) {
- if ((m = GetMode(a)) & 0004) {
- if (S_ISDIR(m)) {
- if (path[pathlen - 1] == '/') {
- if ((p = TryIndex(path, pathlen))) {
- return p;
- } else {
- LockInc(&shared->forbiddens);
- LOGF("directory %`'.*s lacks index page", pathlen, path);
- return ServeError(403, "Forbidden");
- }
- } else {
- return RedirectSlash();
- }
- } else {
- return HandleAsset(a, path, pathlen);
- }
- } else {
- LockInc(&shared->forbiddens);
- LOGF("asset %`'.*s %#o isn't readable", pathlen, path, m);
- return ServeError(403, "Forbidden");
- }
- } else if ((r = FindRedirect(path, pathlen)) != -1) {
- return HandleRedirect(redirects.p + r);
- } else {
- return NULL;
- }
-}
-
-static char *TryHost(const char *host, size_t hostlen) {
- size_t hn;
- char *hp, *p;
- hn = 1 + hostlen + url.path.n;
- hp = FreeLater(xmalloc(3 + 1 + hn));
- hp[0] = '/';
- mempcpy(mempcpy(hp + 1, host, hostlen), url.path.p, url.path.n);
- if ((p = TryPath(hp, hn))) return p;
- if (ParseIp(host, hostlen) == -1) {
- if (hostlen > 4 && !memcmp(host, "www.", 4)) {
- mempcpy(mempcpy(hp + 1, host + 4, hostlen - 4), url.path.p, url.path.n);
- if ((p = TryPath(hp, hn - 4))) return p;
- } else {
- mempcpy(mempcpy(mempcpy(hp + 1, "www.", 4), host, hostlen), url.path.p,
- url.path.n);
- if ((p = TryPath(hp, hn + 4))) return p;
- }
- }
- return NULL;
-}
-
static char *HandleRequest(void) {
char *p;
if (msg.version < 10) {
@@ -4051,22 +4146,8 @@ static char *HandleRequest(void) {
FreeLater(EncodeUrl(&url, 0)), HeaderLength(kHttpReferer),
HeaderData(kHttpReferer), HeaderLength(kHttpUserAgent),
HeaderData(kHttpUserAgent));
- if (url.host.n && (p = TryHost(url.host.p, url.host.n))) {
- return p;
- }
- if (SlicesEqual(url.path.p, url.path.n, "/", 1)) {
- if ((p = TryIndex("/", 1))) return p;
- LockInc(&shared->listingrequests);
- return ServeListing();
- } else if ((p = TryPath(url.path.p, url.path.n))) {
- return p;
- } else if (SlicesEqual(url.path.p, url.path.n, "/statusz", 8)) {
- LockInc(&shared->statuszrequests);
- return ServeStatusz();
- } else {
- LockInc(&shared->notfounds);
- return ServeError(404, "Not Found");
- }
+ if (hasluaglobalhandler) return OnHttpRequest();
+ return Route(url.host.p, url.host.n, url.path.p, url.path.n);
}
static bool HandleMessage(void) {
@@ -4153,11 +4234,13 @@ static bool HandleMessage(void) {
static void InitRequest(void) {
frags = 0;
+ gzipped = 0;
+ branded = 0;
+ content = 0;
msgsize = 0;
+ loops.n = 0;
outbuf.n = 0;
- content = NULL;
- gzipped = false;
- branded = false;
+ luaheaderp = 0;
contentlength = 0;
InitHttpRequest(&msg);
}
@@ -4269,9 +4352,6 @@ static void HandleConnection(void) {
if (uniprocess) {
pid = -1;
connectionclose = true;
- } else if (warmups) {
- --warmups;
- pid = -1;
} else {
switch ((pid = fork())) {
case 0:
@@ -4445,7 +4525,6 @@ void RedBean(int argc, char *argv[], const char *prog) {
hdrbuf.p = xvalloc(hdrbuf.n);
inbuf.n = 64 * 1024;
inbuf.p = xvalloc(inbuf.n);
- SendWarmupRequests();
while (!terminated) {
if (zombied) {
ReapZombies();