2020-09-07 04:39:00 +00:00
|
|
|
/*-*- 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 2020 Justine Alexandra Roberts Tunney │
|
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
│ 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. │
|
2020-09-07 04:39:00 +00:00
|
|
|
│ │
|
2020-12-28 01:18:44 +00:00
|
|
|
│ 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. │
|
2020-09-07 04:39:00 +00:00
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/bits/bits.h"
|
|
|
|
#include "libc/bits/bswap.h"
|
2021-03-27 14:29:55 +00:00
|
|
|
#include "libc/bits/popcnt.h"
|
2021-03-01 07:42:35 +00:00
|
|
|
#include "libc/bits/safemacros.internal.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/calls/calls.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/calls/struct/iovec.h"
|
|
|
|
#include "libc/calls/struct/itimerval.h"
|
|
|
|
#include "libc/calls/struct/stat.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/calls/weirdtypes.h"
|
2021-03-02 07:45:49 +00:00
|
|
|
#include "libc/dce.h"
|
2021-03-07 19:31:44 +00:00
|
|
|
#include "libc/errno.h"
|
2020-12-09 23:04:54 +00:00
|
|
|
#include "libc/fmt/conv.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/fmt/fmt.h"
|
2020-12-09 23:04:54 +00:00
|
|
|
#include "libc/fmt/itoa.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/log/check.h"
|
|
|
|
#include "libc/log/log.h"
|
2021-03-01 07:42:35 +00:00
|
|
|
#include "libc/macros.internal.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/math.h"
|
|
|
|
#include "libc/mem/mem.h"
|
2021-03-27 14:29:55 +00:00
|
|
|
#include "libc/nexgen32e/bsf.h"
|
|
|
|
#include "libc/nexgen32e/bsr.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/nexgen32e/crc32.h"
|
2020-11-13 09:27:49 +00:00
|
|
|
#include "libc/rand/rand.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/runtime/runtime.h"
|
|
|
|
#include "libc/sock/sock.h"
|
|
|
|
#include "libc/stdio/stdio.h"
|
|
|
|
#include "libc/str/str.h"
|
2021-03-25 09:21:13 +00:00
|
|
|
#include "libc/str/thompike.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/str/undeflate.h"
|
2021-03-25 09:21:13 +00:00
|
|
|
#include "libc/str/utf16.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/sysv/consts/af.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/sysv/consts/auxv.h"
|
|
|
|
#include "libc/sysv/consts/clock.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/sysv/consts/ex.h"
|
|
|
|
#include "libc/sysv/consts/exit.h"
|
|
|
|
#include "libc/sysv/consts/f.h"
|
|
|
|
#include "libc/sysv/consts/fd.h"
|
|
|
|
#include "libc/sysv/consts/inaddr.h"
|
|
|
|
#include "libc/sysv/consts/ipproto.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/sysv/consts/itimer.h"
|
|
|
|
#include "libc/sysv/consts/map.h"
|
|
|
|
#include "libc/sysv/consts/o.h"
|
|
|
|
#include "libc/sysv/consts/prot.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/sysv/consts/sa.h"
|
|
|
|
#include "libc/sysv/consts/shut.h"
|
|
|
|
#include "libc/sysv/consts/sig.h"
|
|
|
|
#include "libc/sysv/consts/so.h"
|
|
|
|
#include "libc/sysv/consts/sock.h"
|
|
|
|
#include "libc/sysv/consts/sol.h"
|
2020-10-11 04:18:53 +00:00
|
|
|
#include "libc/sysv/consts/tcp.h"
|
2021-03-25 09:21:13 +00:00
|
|
|
#include "libc/sysv/consts/w.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/sysv/errfuns.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "libc/time/struct/tm.h"
|
|
|
|
#include "libc/time/time.h"
|
|
|
|
#include "libc/x/x.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "libc/zip.h"
|
2020-11-28 20:01:51 +00:00
|
|
|
#include "libc/zipos/zipos.internal.h"
|
2021-03-25 09:21:13 +00:00
|
|
|
#include "net/http/escape.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
#include "net/http/http.h"
|
|
|
|
#include "third_party/getopt/getopt.h"
|
2021-03-22 00:27:53 +00:00
|
|
|
#include "third_party/lua/lauxlib.h"
|
|
|
|
#include "third_party/lua/ltests.h"
|
|
|
|
#include "third_party/lua/lua.h"
|
|
|
|
#include "third_party/lua/lualib.h"
|
2020-10-06 06:11:49 +00:00
|
|
|
#include "third_party/zlib/zlib.h"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
#define USAGE \
|
2021-03-27 14:29:55 +00:00
|
|
|
" [-hvdsm] [-p PORT] [-- SCRIPTARGS...]\n\
|
2020-10-06 06:11:49 +00:00
|
|
|
\n\
|
|
|
|
DESCRIPTION\n\
|
|
|
|
\n\
|
2021-03-25 09:21:13 +00:00
|
|
|
redbean - single-file distributable web server\n\
|
2020-10-06 06:11:49 +00:00
|
|
|
\n\
|
|
|
|
FLAGS\n\
|
|
|
|
\n\
|
|
|
|
-h help\n\
|
|
|
|
-v verbosity\n\
|
|
|
|
-d daemonize\n\
|
2021-03-02 07:45:49 +00:00
|
|
|
-u uniprocess\n\
|
2021-02-27 18:47:19 +00:00
|
|
|
-z print port\n\
|
2020-10-06 06:11:49 +00:00
|
|
|
-m log messages\n\
|
|
|
|
-c INT cache seconds\n\
|
|
|
|
-r /X=/Y redirect X to Y\n\
|
|
|
|
-l ADDR listen ip [default 0.0.0.0]\n\
|
|
|
|
-p PORT listen port [default 8080]\n\
|
|
|
|
-L PATH log file location\n\
|
|
|
|
-P PATH pid file location\n\
|
|
|
|
-U INT daemon set user id\n\
|
|
|
|
-G INT daemon set group id\n\
|
|
|
|
-B STR changes server header\n\
|
|
|
|
\n\
|
|
|
|
FEATURES\n\
|
|
|
|
\n\
|
2021-03-27 14:29:55 +00:00
|
|
|
- Lua v5.4\n\
|
2021-03-25 09:21:13 +00:00
|
|
|
- HTTP v0.9\n\
|
|
|
|
- HTTP v1.0\n\
|
2020-10-06 06:11:49 +00:00
|
|
|
- HTTP v1.1\n\
|
2021-03-27 14:29:55 +00:00
|
|
|
- Content-Encoding\n\
|
2020-10-06 06:11:49 +00:00
|
|
|
- Range / Content-Range\n\
|
|
|
|
- Last-Modified / If-Modified-Since\n\
|
|
|
|
\n\
|
|
|
|
USAGE\n\
|
|
|
|
\n\
|
|
|
|
This executable is also a ZIP file that contains static assets.\n\
|
|
|
|
\n\
|
|
|
|
unzip -vl redbean.com # shows listing of zip contents\n\
|
|
|
|
\n\
|
|
|
|
Audio video content should not be compressed in your ZIP files.\n\
|
|
|
|
Uncompressed assets enable browsers to send Range HTTP request.\n\
|
|
|
|
On the other hand compressed assets are best for gzip encoding.\n\
|
|
|
|
\n\
|
|
|
|
zip redbean.com index.html # adds file\n\
|
|
|
|
zip -0 redbean.com video.mp4 # adds without compression\n\
|
2021-03-25 09:21:13 +00:00
|
|
|
\n\
|
|
|
|
You can run redbean interactively in your terminal as follows:\n\
|
|
|
|
\n\
|
|
|
|
redbean.com -vv\n\
|
|
|
|
CTRL-C # 1x: graceful shutdown\n\
|
|
|
|
CTRL-C # 2x: forceful shutdown\n\
|
|
|
|
\n\
|
|
|
|
You can have redbean run as a daemon by doing the following:\n\
|
|
|
|
\n\
|
|
|
|
redbean.com -vv -d -L redbean.log -P redbean.pid\n\
|
|
|
|
kill -TERM $(cat redbean.pid) # 1x: graceful shutdown\n\
|
|
|
|
kill -TERM $(cat redbean.pid) # 2x: forceful shutdown\n\
|
2021-03-02 08:38:11 +00:00
|
|
|
\n\
|
2021-03-25 09:21:13 +00:00
|
|
|
redbean imposes a 32kb limit on requests to limit the memory of\n\
|
|
|
|
connection processes, which grow to whatever number your system\n\
|
|
|
|
limits and tcp stack configuration allow. If fork() should fail\n\
|
|
|
|
then redbean starts shutting idle connections down.\n\
|
2021-03-02 08:38:11 +00:00
|
|
|
\n\
|
2021-03-25 09:21:13 +00:00
|
|
|
Redirects emit a 307 response unless the location exists in\n\
|
|
|
|
the zip directory, in which case it transparently rewrites.\n\
|
2021-03-02 08:38:11 +00:00
|
|
|
\n\
|
2020-10-06 06:11:49 +00:00
|
|
|
\n"
|
2020-09-07 04:39:00 +00:00
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
#define HASH_LOAD_FACTOR /* 1. / */ 4
|
|
|
|
#define DEFAULT_PORT 8080
|
2020-10-06 06:11:49 +00:00
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
#define AppendHeaderName(p, s) stpcpy(stpcpy(p, s), ": ")
|
2020-10-06 06:11:49 +00:00
|
|
|
|
|
|
|
static const struct itimerval kHeartbeat = {
|
|
|
|
{0, 500000},
|
|
|
|
{0, 500000},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint8_t kGzipHeader[] = {
|
|
|
|
0x1F, // MAGNUM
|
|
|
|
0x8B, // MAGNUM
|
|
|
|
0x08, // CM: DEFLATE
|
|
|
|
0x00, // FLG: NONE
|
|
|
|
0x00, // MTIME: NONE
|
|
|
|
0x00, //
|
|
|
|
0x00, //
|
|
|
|
0x00, //
|
|
|
|
0x00, // XFL
|
|
|
|
kZipOsUnix, // OS
|
|
|
|
};
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static const char kHexToInt[256] = {
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20
|
|
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0x30
|
|
|
|
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50
|
|
|
|
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xe0
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0
|
|
|
|
};
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
static const struct ContentTypeExtension {
|
|
|
|
unsigned char ext[8];
|
|
|
|
const char *mime;
|
|
|
|
} kContentTypeExtension[] = {
|
|
|
|
{"S", "text/plain"}, //
|
|
|
|
{"bmp", "image/x-ms-bmp"}, //
|
|
|
|
{"c", "text/plain"}, //
|
|
|
|
{"cc", "text/plain"}, //
|
|
|
|
{"css", "text/css"}, //
|
|
|
|
{"csv", "text/csv"}, //
|
|
|
|
{"gif", "image/gif"}, //
|
|
|
|
{"h", "text/plain"}, //
|
|
|
|
{"html", "text/html"}, //
|
|
|
|
{"i", "text/plain"}, //
|
|
|
|
{"ico", "image/vnd.microsoft.icon"}, //
|
|
|
|
{"jpeg", "image/jpeg"}, //
|
|
|
|
{"jpg", "image/jpeg"}, //
|
|
|
|
{"js", "application/javascript"}, //
|
|
|
|
{"json", "application/json"}, //
|
|
|
|
{"m4a", "audio/mpeg"}, //
|
|
|
|
{"mp2", "audio/mpeg"}, //
|
|
|
|
{"mp3", "audio/mpeg"}, //
|
|
|
|
{"mp4", "video/mp4"}, //
|
|
|
|
{"mpg", "video/mpeg"}, //
|
|
|
|
{"otf", "font/otf"}, //
|
|
|
|
{"pdf", "application/pdf"}, //
|
|
|
|
{"png", "image/png"}, //
|
|
|
|
{"s", "text/plain"}, //
|
|
|
|
{"svg", "image/svg+xml"}, //
|
|
|
|
{"tiff", "image/tiff"}, //
|
|
|
|
{"ttf", "font/ttf"}, //
|
|
|
|
{"txt", "text/plain"}, //
|
|
|
|
{"wav", "audio/x-wav"}, //
|
|
|
|
{"woff", "font/woff"}, //
|
|
|
|
{"woff2", "font/woff2"}, //
|
|
|
|
{"xml", "application/xml"}, //
|
|
|
|
{"zip", "application/zip"}, //
|
|
|
|
};
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
struct Buffer {
|
|
|
|
size_t n;
|
|
|
|
char *p;
|
|
|
|
};
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
struct Parser {
|
2021-03-25 09:21:13 +00:00
|
|
|
int i;
|
|
|
|
int c;
|
|
|
|
const char *data;
|
|
|
|
int size;
|
|
|
|
char *p;
|
|
|
|
char *q;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Params {
|
|
|
|
size_t n;
|
|
|
|
struct Param {
|
|
|
|
struct Buffer key;
|
|
|
|
struct Buffer val; // val.n may be SIZE_MAX
|
|
|
|
} * p;
|
|
|
|
};
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
struct Request {
|
|
|
|
struct Buffer path;
|
|
|
|
struct Params params;
|
|
|
|
struct Buffer fragment;
|
|
|
|
};
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static struct Freelist {
|
|
|
|
size_t n;
|
|
|
|
void **p;
|
|
|
|
} freelist;
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
static struct Redirects {
|
2021-03-25 09:21:13 +00:00
|
|
|
size_t n;
|
2020-10-06 06:11:49 +00:00
|
|
|
struct Redirect {
|
|
|
|
const char *path;
|
|
|
|
size_t pathlen;
|
2021-03-25 09:21:13 +00:00
|
|
|
const char *location;
|
2020-10-06 06:11:49 +00:00
|
|
|
} * p;
|
|
|
|
} redirects;
|
|
|
|
|
|
|
|
static struct Assets {
|
|
|
|
uint32_t n;
|
|
|
|
struct Asset {
|
2021-03-25 09:21:13 +00:00
|
|
|
uint32_t lf;
|
2020-10-06 06:11:49 +00:00
|
|
|
uint32_t hash;
|
|
|
|
int64_t lastmodified;
|
|
|
|
char *lastmodifiedstr;
|
|
|
|
} * p;
|
|
|
|
} assets;
|
|
|
|
|
|
|
|
static bool killed;
|
2021-03-25 09:21:13 +00:00
|
|
|
static bool istext;
|
|
|
|
static bool zombied;
|
|
|
|
static bool gzipped;
|
|
|
|
static bool branded;
|
|
|
|
static bool meltdown;
|
|
|
|
static bool heartless;
|
2021-02-27 18:47:19 +00:00
|
|
|
static bool printport;
|
2020-10-06 06:11:49 +00:00
|
|
|
static bool heartbeat;
|
|
|
|
static bool daemonize;
|
|
|
|
static bool terminated;
|
|
|
|
static bool uniprocess;
|
|
|
|
static bool invalidated;
|
|
|
|
static bool logmessages;
|
2021-03-25 09:21:13 +00:00
|
|
|
static bool checkedmethod;
|
|
|
|
static bool connectionclose;
|
|
|
|
static bool keyboardinterrupt;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static int frags;
|
2020-10-06 06:11:49 +00:00
|
|
|
static int gmtoff;
|
|
|
|
static int server;
|
|
|
|
static int client;
|
|
|
|
static int daemonuid;
|
|
|
|
static int daemongid;
|
2021-03-25 09:21:13 +00:00
|
|
|
static int statuscode;
|
|
|
|
static unsigned httpversion;
|
2020-10-06 06:11:49 +00:00
|
|
|
static uint32_t clientaddrsize;
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static lua_State *L;
|
|
|
|
static void *content;
|
2021-03-27 14:29:55 +00:00
|
|
|
static uint8_t *zmap;
|
|
|
|
static uint8_t *zdir;
|
|
|
|
static size_t hdrsize;
|
2021-03-25 09:21:13 +00:00
|
|
|
static size_t msgsize;
|
|
|
|
static size_t amtread;
|
2020-10-06 06:11:49 +00:00
|
|
|
static size_t zmapsize;
|
2021-03-25 09:21:13 +00:00
|
|
|
static char *luaheaderp;
|
2020-10-06 06:11:49 +00:00
|
|
|
static const char *pidpath;
|
|
|
|
static const char *logpath;
|
|
|
|
static int64_t programtime;
|
2021-03-25 09:21:13 +00:00
|
|
|
static size_t contentlength;
|
|
|
|
static int64_t cacheseconds;
|
|
|
|
static uint8_t gzip_footer[8];
|
2020-10-06 06:11:49 +00:00
|
|
|
static const char *serverheader;
|
2021-03-25 09:21:13 +00:00
|
|
|
|
|
|
|
static struct Buffer inbuf;
|
|
|
|
static struct Buffer hdrbuf;
|
|
|
|
static struct Buffer outbuf;
|
2021-03-27 14:29:55 +00:00
|
|
|
static struct Request request;
|
2020-10-06 06:11:49 +00:00
|
|
|
|
|
|
|
static long double nowish;
|
2021-03-25 09:21:13 +00:00
|
|
|
static long double startread;
|
2020-10-06 06:11:49 +00:00
|
|
|
static long double startrequest;
|
|
|
|
static long double startconnection;
|
|
|
|
static struct sockaddr_in serveraddr;
|
|
|
|
static struct sockaddr_in clientaddr;
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static struct HttpRequest msg;
|
2020-10-06 06:11:49 +00:00
|
|
|
static char currentdate[32];
|
|
|
|
static char clientaddrstr[32];
|
|
|
|
static char serveraddrstr[32];
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void OnChld(void) {
|
|
|
|
zombied = true;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void OnAlrm(void) {
|
2020-10-06 06:11:49 +00:00
|
|
|
heartbeat = true;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void OnUsr1(void) {
|
|
|
|
invalidated = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnUsr2(void) {
|
|
|
|
meltdown = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnTerm(void) {
|
|
|
|
if (!terminated) {
|
2020-10-06 06:11:49 +00:00
|
|
|
terminated = true;
|
2021-03-25 09:21:13 +00:00
|
|
|
} else {
|
|
|
|
killed = true;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void OnInt(void) {
|
|
|
|
keyboardinterrupt = true;
|
|
|
|
OnTerm();
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void OnHup(void) {
|
|
|
|
if (daemonize) {
|
|
|
|
OnUsr1();
|
|
|
|
} else {
|
|
|
|
OnTerm();
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static int CompareSlices(const char *a, size_t n, const char *b, size_t m) {
|
2021-03-25 09:21:13 +00:00
|
|
|
int c;
|
|
|
|
if ((c = memcmp(a, b, MIN(n, m)))) return c;
|
|
|
|
if (n < m) return -1;
|
|
|
|
if (n > m) return +1;
|
|
|
|
return 0;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static int CompareSlicesCase(const char *a, size_t n, const char *b, size_t m) {
|
|
|
|
int c;
|
|
|
|
if ((c = memcasecmp(a, b, MIN(n, m)))) return c;
|
|
|
|
if (n < m) return -1;
|
|
|
|
if (n > m) return +1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static long FindRedirect(const char *path, size_t n) {
|
2020-10-06 06:11:49 +00:00
|
|
|
int c, m, l, r, z;
|
|
|
|
l = 0;
|
2021-03-25 09:21:13 +00:00
|
|
|
r = redirects.n - 1;
|
2020-10-06 06:11:49 +00:00
|
|
|
while (l <= r) {
|
|
|
|
m = (l + r) >> 1;
|
2021-03-27 14:29:55 +00:00
|
|
|
c = CompareSlices(redirects.p[m].path, redirects.p[m].pathlen, path, n);
|
2020-10-06 06:11:49 +00:00
|
|
|
if (c < 0) {
|
|
|
|
l = m + 1;
|
|
|
|
} else if (c > 0) {
|
|
|
|
r = m - 1;
|
|
|
|
} else {
|
2021-03-25 09:21:13 +00:00
|
|
|
return m;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddRedirect(const char *arg) {
|
|
|
|
long i, j;
|
|
|
|
const char *p;
|
|
|
|
struct Redirect r;
|
|
|
|
CHECK_NOTNULL((p = strchr(arg, '=')));
|
|
|
|
CHECK_GT(p - arg, 0);
|
|
|
|
r.path = arg;
|
|
|
|
r.pathlen = p - arg;
|
|
|
|
r.location = p + 1;
|
|
|
|
if ((i = FindRedirect(r.path, r.pathlen)) != -1) {
|
|
|
|
redirects.p[i] = r;
|
|
|
|
} else {
|
|
|
|
i = redirects.n;
|
|
|
|
redirects.p = xrealloc(redirects.p, (i + 1) * sizeof(*redirects.p));
|
|
|
|
for (j = i; j > 0; --j) {
|
2021-03-27 14:29:55 +00:00
|
|
|
if (CompareSlices(r.path, r.pathlen, redirects.p[j - 1].path,
|
|
|
|
redirects.p[j - 1].pathlen) < 0) {
|
2021-03-25 09:21:13 +00:00
|
|
|
redirects.p[j] = redirects.p[j - 1];
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
redirects.p[j] = r;
|
|
|
|
++redirects.n;
|
|
|
|
}
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
static int CompareInts(const uint64_t x, uint64_t y) {
|
|
|
|
return x > y ? 1 : x < y ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static const char *FindContentType(uint64_t ext) {
|
2020-10-06 06:11:49 +00:00
|
|
|
int c, m, l, r;
|
|
|
|
l = 0;
|
|
|
|
r = ARRAYLEN(kContentTypeExtension) - 1;
|
|
|
|
while (l <= r) {
|
|
|
|
m = (l + r) >> 1;
|
|
|
|
c = CompareInts(READ64BE(kContentTypeExtension[m].ext), ext);
|
|
|
|
if (c < 0) {
|
|
|
|
l = m + 1;
|
|
|
|
} else if (c > 0) {
|
|
|
|
r = m - 1;
|
|
|
|
} else {
|
|
|
|
return kContentTypeExtension[m].mime;
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
return NULL;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *GetContentType(const char *path, size_t n) {
|
|
|
|
size_t i;
|
|
|
|
uint64_t x;
|
2021-03-25 09:21:13 +00:00
|
|
|
const char *p, *r;
|
2020-10-06 06:11:49 +00:00
|
|
|
if ((p = memrchr(path, '.', n))) {
|
|
|
|
for (x = 0, i = n; i-- > p + 1 - path;) {
|
|
|
|
x <<= 8;
|
|
|
|
x |= path[i] & 0xFF;
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
if ((r = FindContentType(bswap_64(x)))) {
|
|
|
|
return r;
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
return "application/octet-stream";
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void DescribeAddress(char buf[32], const struct sockaddr_in *addr) {
|
|
|
|
char *p = buf;
|
|
|
|
const uint8_t *ip4 = (const uint8_t *)&addr->sin_addr.s_addr;
|
|
|
|
p += uint64toarray_radix10(ip4[0], p), *p++ = '.';
|
|
|
|
p += uint64toarray_radix10(ip4[1], p), *p++ = '.';
|
|
|
|
p += uint64toarray_radix10(ip4[2], p), *p++ = '.';
|
|
|
|
p += uint64toarray_radix10(ip4[3], p), *p++ = ':';
|
|
|
|
p += uint64toarray_radix10(ntohs(addr->sin_port), p);
|
|
|
|
*p = '\0';
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void SetDefaults(void) {
|
|
|
|
cacheseconds = -1;
|
2021-03-27 05:31:41 +00:00
|
|
|
serverheader = "redbean/0.2";
|
2020-09-07 04:39:00 +00:00
|
|
|
serveraddr.sin_family = AF_INET;
|
2020-10-06 06:11:49 +00:00
|
|
|
serveraddr.sin_port = htons(DEFAULT_PORT);
|
2020-09-07 04:39:00 +00:00
|
|
|
serveraddr.sin_addr.s_addr = INADDR_ANY;
|
2021-03-25 09:21:13 +00:00
|
|
|
AddRedirect("/=/tool/net/redbean.html");
|
|
|
|
AddRedirect("/index.html=/tool/net/redbean.html");
|
|
|
|
AddRedirect("/favicon.ico=/tool/net/redbean.ico");
|
|
|
|
}
|
|
|
|
|
2021-03-27 05:31:41 +00:00
|
|
|
static wontreturn void PrintUsage(FILE *f, int rc) {
|
|
|
|
fprintf(f, "SYNOPSIS\n\n %s%s", program_invocation_name, USAGE);
|
|
|
|
exit(rc);
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void GetOpts(int argc, char *argv[]) {
|
|
|
|
int opt;
|
2021-02-27 18:47:19 +00:00
|
|
|
while ((opt = getopt(argc, argv, "zhduvml:p:w:r:c:L:P:U:G:B:")) != -1) {
|
2020-09-07 04:39:00 +00:00
|
|
|
switch (opt) {
|
2020-10-06 06:11:49 +00:00
|
|
|
case 'v':
|
2021-03-01 07:42:35 +00:00
|
|
|
__log_level++;
|
2020-10-06 06:11:49 +00:00
|
|
|
break;
|
2020-09-07 04:39:00 +00:00
|
|
|
case 'd':
|
|
|
|
daemonize = true;
|
|
|
|
break;
|
2020-10-06 06:11:49 +00:00
|
|
|
case 'u':
|
|
|
|
uniprocess = true;
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
logmessages = true;
|
|
|
|
break;
|
2021-02-27 18:47:19 +00:00
|
|
|
case 'z':
|
|
|
|
printport = true;
|
|
|
|
break;
|
2020-10-06 06:11:49 +00:00
|
|
|
case 'r':
|
|
|
|
AddRedirect(optarg);
|
|
|
|
break;
|
|
|
|
case 'c':
|
2021-03-25 09:21:13 +00:00
|
|
|
cacheseconds = strtol(optarg, NULL, 0);
|
2020-09-07 04:39:00 +00:00
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
CHECK_NE(0xFFFF, (serveraddr.sin_port = htons(parseport(optarg))));
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
CHECK_EQ(1, inet_pton(AF_INET, optarg, &serveraddr.sin_addr));
|
|
|
|
break;
|
2020-10-06 06:11:49 +00:00
|
|
|
case 'B':
|
|
|
|
serverheader = optarg;
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
logpath = optarg;
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
pidpath = optarg;
|
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
daemonuid = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 'G':
|
2021-03-27 05:31:41 +00:00
|
|
|
daemongid = atoi(optarg);
|
2020-10-06 06:11:49 +00:00
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
PrintUsage(stdout, EXIT_SUCCESS);
|
2020-09-07 04:39:00 +00:00
|
|
|
default:
|
2020-10-06 06:11:49 +00:00
|
|
|
PrintUsage(stderr, EX_USAGE);
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
if (logpath) {
|
|
|
|
CHECK_NOTNULL(freopen(logpath, "a", stderr));
|
|
|
|
}
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
static void Daemonize(void) {
|
|
|
|
char ibuf[21];
|
|
|
|
int i, fd, pid;
|
|
|
|
for (i = 0; i < 128; ++i) close(i);
|
2021-03-02 08:38:11 +00:00
|
|
|
if ((pid = fork()) > 0) exit(0);
|
|
|
|
setsid();
|
|
|
|
if ((pid = fork()) > 0) _exit(0);
|
|
|
|
umask(0);
|
2020-10-06 06:11:49 +00:00
|
|
|
if (pidpath) {
|
2021-03-02 08:38:11 +00:00
|
|
|
fd = open(pidpath, O_CREAT | O_EXCL | O_WRONLY, 0644);
|
|
|
|
write(fd, ibuf, uint64toarray_radix10(getpid(), ibuf));
|
|
|
|
close(fd);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
if (!logpath) logpath = "/dev/null";
|
2021-03-02 08:38:11 +00:00
|
|
|
freopen("/dev/null", "r", stdin);
|
|
|
|
freopen(logpath, "a", stdout);
|
|
|
|
freopen(logpath, "a", stderr);
|
2021-03-27 05:31:41 +00:00
|
|
|
LOGIFNEG1(setuid(daemonuid));
|
|
|
|
LOGIFNEG1(setgid(daemongid));
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void OnWorkerExit(int pid, int ws) {
|
|
|
|
if (WIFEXITED(ws)) {
|
|
|
|
if (WEXITSTATUS(ws)) {
|
|
|
|
WARNF("worker %d exited with %d", pid, WEXITSTATUS(ws));
|
|
|
|
} else {
|
|
|
|
DEBUGF("worker %d exited", pid);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
WARNF("worker %d terminated with %s", pid, strsignal(WTERMSIG(ws)));
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void WaitAll(void) {
|
2021-03-25 09:21:13 +00:00
|
|
|
int ws, pid;
|
2020-10-06 06:11:49 +00:00
|
|
|
for (;;) {
|
2021-03-25 09:21:13 +00:00
|
|
|
if ((pid = wait(&ws)) != -1) {
|
|
|
|
OnWorkerExit(pid, ws);
|
|
|
|
} else {
|
|
|
|
if (errno == ECHILD) break;
|
|
|
|
if (errno == EINTR) {
|
|
|
|
if (killed) {
|
|
|
|
WARNF("%s terminating harder", serveraddrstr);
|
|
|
|
LOGIFNEG1(kill(0, SIGTERM));
|
|
|
|
}
|
|
|
|
continue;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
FATALF("%s wait error %s", serveraddrstr, strerror(errno));
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void ReapZombies(void) {
|
|
|
|
int ws, pid;
|
|
|
|
zombied = false;
|
|
|
|
do {
|
|
|
|
if ((pid = waitpid(-1, &ws, WNOHANG)) != -1) {
|
|
|
|
if (pid) {
|
|
|
|
OnWorkerExit(pid, ws);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (errno == ECHILD) break;
|
|
|
|
if (errno == EINTR) continue;
|
|
|
|
FATALF("%s wait error %s", serveraddrstr, strerror(errno));
|
|
|
|
}
|
|
|
|
} while (!terminated);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) {
|
2020-09-07 04:39:00 +00:00
|
|
|
ssize_t rc;
|
2020-10-06 06:11:49 +00:00
|
|
|
size_t wrote;
|
2020-09-07 04:39:00 +00:00
|
|
|
do {
|
2020-10-06 06:11:49 +00:00
|
|
|
if ((rc = writev(fd, iov, iovlen)) != -1) {
|
2020-09-07 04:39:00 +00:00
|
|
|
wrote = rc;
|
2020-10-06 06:11:49 +00:00
|
|
|
do {
|
|
|
|
if (wrote >= iov->iov_len) {
|
|
|
|
wrote -= iov->iov_len;
|
|
|
|
++iov;
|
|
|
|
--iovlen;
|
|
|
|
} else {
|
|
|
|
iov->iov_base = (char *)iov->iov_base + wrote;
|
|
|
|
iov->iov_len -= wrote;
|
|
|
|
wrote = 0;
|
|
|
|
}
|
|
|
|
} while (wrote);
|
|
|
|
} else if (errno == EINTR) {
|
|
|
|
if (killed) return -1;
|
2020-09-07 04:39:00 +00:00
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
} while (iovlen);
|
2020-09-07 04:39:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
static uint32_t Hash(const void *data, size_t size) {
|
|
|
|
uint32_t h;
|
|
|
|
h = crc32c(0, data, size);
|
|
|
|
if (!h) h = 1;
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool HasHeader(int h) {
|
2021-03-27 14:29:55 +00:00
|
|
|
return msg.headers[h].b > msg.headers[h].a;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CompareHeader(int h, const char *s) {
|
|
|
|
return CompareSlices(s, strlen(s), inbuf.p + msg.headers[h].a,
|
|
|
|
msg.headers[h].b - msg.headers[h].a);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static bool HeaderEquals(int h, const char *s) {
|
|
|
|
return !CompareHeader(h, s);
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool ClientAcceptsGzip(void) {
|
|
|
|
return httpversion >= 100 &&
|
2021-03-27 14:29:55 +00:00
|
|
|
!!memmem(inbuf.p + msg.headers[kHttpAcceptEncoding].a,
|
|
|
|
msg.headers[kHttpAcceptEncoding].b -
|
|
|
|
msg.headers[kHttpAcceptEncoding].a,
|
2021-03-25 09:21:13 +00:00
|
|
|
"gzip", 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateCurrentDate(long double now) {
|
|
|
|
int64_t t;
|
|
|
|
struct tm tm;
|
|
|
|
t = nowish = now;
|
|
|
|
gmtime_r(&t, &tm);
|
|
|
|
FormatHttpDateTime(currentdate, &tm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t GetGmtOffset(void) {
|
2020-10-06 06:11:49 +00:00
|
|
|
int64_t t;
|
|
|
|
struct tm tm;
|
|
|
|
t = nowl();
|
|
|
|
localtime_r(&t, &tm);
|
|
|
|
return tm.tm_gmtoff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t LocoTimeToZulu(int64_t x) {
|
|
|
|
return x - gmtoff;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static int64_t GetLastModifiedZip(const uint8_t *cfile) {
|
|
|
|
const uint8_t *p, *pe;
|
2020-10-06 06:11:49 +00:00
|
|
|
for (p = ZIP_CFILE_EXTRA(cfile), pe = p + ZIP_CFILE_EXTRASIZE(cfile); p < pe;
|
|
|
|
p += ZIP_EXTRA_SIZE(p)) {
|
|
|
|
if (ZIP_EXTRA_HEADERID(p) == kZipExtraNtfs) {
|
|
|
|
return LocoTimeToZulu(READ64LE(ZIP_EXTRA_CONTENT(p) + 8) /
|
|
|
|
HECTONANOSECONDS -
|
|
|
|
MODERNITYSECONDS);
|
|
|
|
} else if (ZIP_EXTRA_HEADERID(p) == kZipExtraExtendedTimestamp) {
|
|
|
|
return READ32LE(ZIP_EXTRA_CONTENT(p) + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return LocoTimeToZulu(DosDateTimeToUnix(ZIP_CFILE_LASTMODIFIEDDATE(cfile),
|
|
|
|
ZIP_CFILE_LASTMODIFIEDTIME(cfile)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsCompressed(struct Asset *a) {
|
2021-03-27 14:29:55 +00:00
|
|
|
return ZIP_LFILE_COMPRESSIONMETHOD(zmap + a->lf) == kZipCompressionDeflate;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsNotModified(struct Asset *a) {
|
2021-03-25 09:21:13 +00:00
|
|
|
if (httpversion < 100) return false;
|
2020-10-06 06:11:49 +00:00
|
|
|
if (!HasHeader(kHttpIfModifiedSince)) return false;
|
|
|
|
return a->lastmodified >=
|
2021-03-27 14:29:55 +00:00
|
|
|
ParseHttpDateTime(inbuf.p + msg.headers[kHttpIfModifiedSince].a,
|
|
|
|
msg.headers[kHttpIfModifiedSince].b -
|
|
|
|
msg.headers[kHttpIfModifiedSince].a);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *FormatUnixHttpDateTime(char *s, int64_t t) {
|
|
|
|
struct tm tm;
|
|
|
|
gmtime_r(&t, &tm);
|
|
|
|
FormatHttpDateTime(s, &tm);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void IndexAssets(void) {
|
2020-10-06 06:11:49 +00:00
|
|
|
int64_t lm;
|
|
|
|
struct Asset *p;
|
|
|
|
uint32_t i, n, m, cf, step, hash;
|
2021-03-27 14:29:55 +00:00
|
|
|
CHECK_GE(HASH_LOAD_FACTOR, 2);
|
|
|
|
n = ZIP_CDIR_RECORDS(zdir);
|
2020-10-06 06:11:49 +00:00
|
|
|
m = roundup2pow(MAX(1, n) * HASH_LOAD_FACTOR);
|
2021-03-27 14:29:55 +00:00
|
|
|
p = xcalloc(m, sizeof(struct Asset));
|
|
|
|
CHECK_EQ(kZipCdirHdrMagic, ZIP_CDIR_MAGIC(zdir));
|
|
|
|
for (cf = ZIP_CDIR_OFFSET(zdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
|
|
|
|
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
|
|
|
|
hash = Hash(ZIP_CFILE_NAME(zmap + cf), ZIP_CFILE_NAMESIZE(zmap + cf));
|
|
|
|
step = 0;
|
|
|
|
do {
|
|
|
|
i = (hash + (step * (step + 1)) >> 1) & (m - 1);
|
|
|
|
++step;
|
|
|
|
} while (p[i].hash);
|
|
|
|
lm = GetLastModifiedZip(zmap + cf);
|
|
|
|
p[i].hash = hash;
|
|
|
|
p[i].lf = ZIP_CFILE_OFFSET(zmap + cf);
|
|
|
|
p[i].lastmodified = lm;
|
|
|
|
p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm);
|
|
|
|
}
|
|
|
|
assets.p = p;
|
|
|
|
assets.n = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OpenZip(const char *path) {
|
2020-10-06 06:11:49 +00:00
|
|
|
int fd;
|
|
|
|
struct stat st;
|
2021-03-27 14:29:55 +00:00
|
|
|
CHECK_NE(-1, (fd = open(path, O_RDONLY)));
|
|
|
|
CHECK_NE(-1, fstat(fd, &st));
|
|
|
|
CHECK((zmapsize = st.st_size));
|
|
|
|
CHECK_NE(MAP_FAILED,
|
|
|
|
(zmap = mmap(NULL, zmapsize, PROT_READ, MAP_SHARED, fd, 0)));
|
|
|
|
CHECK_NOTNULL((zdir = zipfindcentraldir(zmap, zmapsize)));
|
|
|
|
close(fd);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct Asset *FindAsset(const char *path, size_t pathlen) {
|
|
|
|
uint32_t i, step, hash;
|
|
|
|
hash = Hash(path, pathlen);
|
|
|
|
for (step = 0;; ++step) {
|
|
|
|
i = (hash + (step * (step + 1)) >> 1) & (assets.n - 1);
|
|
|
|
if (!assets.p[i].hash) return NULL;
|
|
|
|
if (hash == assets.p[i].hash &&
|
2021-03-27 14:29:55 +00:00
|
|
|
pathlen == ZIP_LFILE_NAMESIZE(zmap + assets.p[i].lf) &&
|
|
|
|
memcmp(path, ZIP_LFILE_NAME(zmap + assets.p[i].lf), pathlen) == 0) {
|
2020-10-06 06:11:49 +00:00
|
|
|
return &assets.p[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static struct Asset *LocateAsset(const char *path, size_t pathlen) {
|
|
|
|
char *path2;
|
|
|
|
struct Asset *a;
|
|
|
|
if (pathlen && path[0] == '/') ++path, --pathlen;
|
|
|
|
if (!(a = FindAsset(path, pathlen)) &&
|
|
|
|
(!pathlen || (pathlen && path[pathlen - 1] == '/'))) {
|
|
|
|
path2 = xmalloc(pathlen + 10);
|
|
|
|
memcpy(path2, path, pathlen);
|
|
|
|
memcpy(path2 + pathlen, "index.lua", 9);
|
|
|
|
if (!(a = FindAsset(path2, pathlen + 9))) {
|
|
|
|
memcpy(path2 + pathlen, "index.html", 10);
|
|
|
|
a = FindAsset(path2, pathlen + 10);
|
|
|
|
}
|
|
|
|
free(path2);
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *FreeLater(void *p) {
|
|
|
|
if (p) {
|
|
|
|
freelist.p = xrealloc(freelist.p, ++freelist.n * sizeof(*freelist.p));
|
|
|
|
freelist.p[freelist.n - 1] = p;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CollectGarbage(void) {
|
|
|
|
size_t i;
|
2021-03-27 14:29:55 +00:00
|
|
|
for (i = 0; i < freelist.n; ++i) free(freelist.p[i]);
|
2021-03-25 09:21:13 +00:00
|
|
|
freelist.n = 0;
|
2021-03-27 14:29:55 +00:00
|
|
|
free(request.params.p);
|
|
|
|
DestroyHttpRequest(&msg);
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void EmitParamKey(struct Parser *u, struct Params *h) {
|
|
|
|
h->p = xrealloc(h->p, ++h->n * sizeof(*h->p));
|
|
|
|
h->p[h->n - 1].key.p = u->q;
|
|
|
|
h->p[h->n - 1].key.n = u->p - u->q;
|
2021-03-25 09:21:13 +00:00
|
|
|
u->q = u->p;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void EmitParamVal(struct Parser *u, struct Params *h, bool t) {
|
2021-03-25 09:21:13 +00:00
|
|
|
if (!t) {
|
|
|
|
if (u->p > u->q) {
|
2021-03-27 14:29:55 +00:00
|
|
|
EmitParamKey(u, h);
|
|
|
|
h->p[h->n - 1].val.p = NULL;
|
|
|
|
h->p[h->n - 1].val.n = SIZE_MAX;
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
} else {
|
2021-03-27 14:29:55 +00:00
|
|
|
h->p[h->n - 1].val.p = u->q;
|
|
|
|
h->p[h->n - 1].val.n = u->p - u->q;
|
2021-03-25 09:21:13 +00:00
|
|
|
u->q = u->p;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void ParseEscape(struct Parser *u) {
|
2021-03-25 09:21:13 +00:00
|
|
|
int a, b;
|
2021-03-27 14:29:55 +00:00
|
|
|
a = u->i < u->size ? u->data[u->i++] & 0xff : 0;
|
|
|
|
b = u->i < u->size ? u->data[u->i++] & 0xff : 0;
|
2021-03-25 09:21:13 +00:00
|
|
|
*u->p++ = kHexToInt[a] << 4 | kHexToInt[b];
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void ParsePath(struct Parser *u, struct Buffer *h) {
|
|
|
|
while (u->i < u->size) {
|
|
|
|
u->c = u->data[u->i++] & 0xff;
|
|
|
|
if (u->c == '#' || u->c == '?') {
|
|
|
|
break;
|
|
|
|
} else if (u->c != '%') {
|
|
|
|
*u->p++ = u->c;
|
|
|
|
} else {
|
|
|
|
ParseEscape(u);
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
h->p = u->q;
|
|
|
|
h->n = u->p - u->q;
|
2021-03-25 09:21:13 +00:00
|
|
|
u->q = u->p;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void ParseParams(struct Parser *u, struct Params *h, bool isform) {
|
|
|
|
bool t = false;
|
|
|
|
while (u->i < u->size) {
|
|
|
|
switch ((u->c = u->data[u->i++] & 0xff)) {
|
|
|
|
default:
|
|
|
|
*u->p++ = u->c;
|
|
|
|
break;
|
|
|
|
case '+':
|
|
|
|
*u->p++ = isform ? ' ' : u->c;
|
|
|
|
break;
|
|
|
|
case '%':
|
|
|
|
ParseEscape(u);
|
|
|
|
break;
|
|
|
|
case '&':
|
|
|
|
EmitParamVal(u, h, t);
|
|
|
|
t = false;
|
|
|
|
break;
|
|
|
|
case '=':
|
|
|
|
if (!t) {
|
|
|
|
if (u->p > u->q) {
|
|
|
|
EmitParamKey(u, h);
|
|
|
|
t = true;
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
} else {
|
|
|
|
*u->p++ = '=';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '#':
|
|
|
|
goto EndOfParams;
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
EndOfParams:
|
|
|
|
EmitParamVal(u, h, t);
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void ParseFragment(struct Parser *u, struct Buffer *h) {
|
|
|
|
while (u->i < u->size) {
|
|
|
|
u->c = u->data[u->i++] & 0xff;
|
|
|
|
if (u->c != '%') {
|
|
|
|
*u->p++ = u->c;
|
|
|
|
} else {
|
|
|
|
ParseEscape(u);
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
h->p = u->q;
|
|
|
|
h->n = u->p - u->q;
|
2021-03-25 09:21:13 +00:00
|
|
|
u->q = u->p;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static bool IsForbiddenPath(struct Buffer *b) {
|
|
|
|
return !!memmem(b->p, b->n, "/.", 2);
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static bool ParseRequestUri(void) {
|
2021-03-27 14:29:55 +00:00
|
|
|
struct Parser u;
|
2021-03-25 09:21:13 +00:00
|
|
|
u.i = 0;
|
|
|
|
u.c = '/';
|
2021-03-27 14:29:55 +00:00
|
|
|
u.data = inbuf.p + msg.uri.a;
|
|
|
|
u.size = msg.uri.b - msg.uri.a;
|
|
|
|
memset(&request, 0, sizeof(request));
|
2021-03-25 09:21:13 +00:00
|
|
|
if (!u.size || *u.data != '/') return false;
|
|
|
|
u.q = u.p = FreeLater(xmalloc(u.size));
|
2021-03-27 14:29:55 +00:00
|
|
|
if (u.c == '/') ParsePath(&u, &request.path);
|
|
|
|
if (u.c == '?') ParseParams(&u, &request.params, false);
|
|
|
|
if (u.c == '#') ParseFragment(&u, &request.fragment);
|
|
|
|
return u.i == u.size && !IsForbiddenPath(&request.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ParseFormParams(void) {
|
|
|
|
struct Parser u;
|
|
|
|
u.i = 0;
|
|
|
|
u.c = 0;
|
|
|
|
u.data = inbuf.p + hdrsize;
|
|
|
|
u.size = msgsize - hdrsize;
|
|
|
|
u.q = u.p = FreeLater(xmalloc(u.size));
|
|
|
|
ParseParams(&u, &request.params, true);
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 06:11:49 +00:00
|
|
|
static void *AddRange(char *content, long start, long length) {
|
|
|
|
intptr_t mend, mstart;
|
|
|
|
if (!__builtin_add_overflow((intptr_t)content, start, &mstart) ||
|
|
|
|
!__builtin_add_overflow(mstart, length, &mend) ||
|
2021-03-27 14:29:55 +00:00
|
|
|
((intptr_t)zmap <= mstart && mstart <= (intptr_t)zmap + zmapsize) ||
|
|
|
|
((intptr_t)zmap <= mend && mend <= (intptr_t)zmap + zmapsize)) {
|
2020-10-06 06:11:49 +00:00
|
|
|
return (void *)mstart;
|
|
|
|
} else {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsConnectionClose(void) {
|
|
|
|
int n;
|
|
|
|
char *p;
|
2021-03-27 14:29:55 +00:00
|
|
|
p = inbuf.p + msg.headers[kHttpConnection].a;
|
|
|
|
n = msg.headers[kHttpConnection].b - msg.headers[kHttpConnection].a;
|
2020-10-06 06:11:49 +00:00
|
|
|
return n == 5 && memcmp(p, "close", 5) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *AppendCrlf(char *p) {
|
2021-03-25 09:21:13 +00:00
|
|
|
p[0] = '\r';
|
|
|
|
p[1] = '\n';
|
|
|
|
return p + 2;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static bool MustNotIncludeMessageBody(void) { /* RFC2616 § 4.4 */
|
2021-03-27 14:29:55 +00:00
|
|
|
return msg.method == kHttpHead || (100 <= statuscode && statuscode <= 199) ||
|
2021-03-25 09:21:13 +00:00
|
|
|
statuscode == 204 || statuscode == 304;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static char *SetStatus(int code, const char *reason) {
|
|
|
|
char *p;
|
|
|
|
statuscode = code;
|
|
|
|
p = hdrbuf.p;
|
|
|
|
p = stpcpy(p, "HTTP/1.");
|
|
|
|
*p++ = httpversion == 100 ? '0' : '1';
|
|
|
|
*p++ = ' ';
|
|
|
|
p += uint64toarray_radix10(code, p);
|
|
|
|
*p++ = ' ';
|
|
|
|
p = stpcpy(p, reason);
|
2020-10-06 06:11:49 +00:00
|
|
|
return AppendCrlf(p);
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static char *AppendHeader(char *p, const char *k, const char *v) {
|
|
|
|
return AppendCrlf(stpcpy(AppendHeaderName(p, k), v));
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *AppendContentType(char *p, const char *ct) {
|
|
|
|
p = AppendHeaderName(p, "Content-Type");
|
|
|
|
p = stpcpy(p, ct);
|
2021-03-25 09:21:13 +00:00
|
|
|
if (startswith(ct, "text/") && !strchr(ct, ';')) {
|
|
|
|
p = stpcpy(p, "; charset=utf-8");
|
|
|
|
istext = true;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
return AppendCrlf(p);
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static char *ServeError(int code, const char *reason) {
|
|
|
|
char *p;
|
|
|
|
size_t reasonlen;
|
|
|
|
reasonlen = strlen(reason);
|
|
|
|
p = SetStatus(code, reason);
|
|
|
|
p = AppendContentType(p, "text/plain");
|
|
|
|
content = FreeLater(xmalloc(reasonlen + 3));
|
|
|
|
contentlength = reasonlen + 2;
|
|
|
|
stpcpy(stpcpy(content, reason), "\r\n");
|
2021-03-27 14:29:55 +00:00
|
|
|
WARNF("%s %s %`'.*s %d %s", clientaddrstr, kHttpMethod[msg.method],
|
|
|
|
request.path.n, request.path.p, code, reason);
|
2021-03-25 09:21:13 +00:00
|
|
|
return p;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *AppendExpires(char *p, int64_t t) {
|
|
|
|
struct tm tm;
|
|
|
|
gmtime_r(&t, &tm);
|
|
|
|
p = AppendHeaderName(p, "Expires");
|
2021-03-25 09:21:13 +00:00
|
|
|
FormatHttpDateTime(p, &tm);
|
|
|
|
p += 29;
|
2020-10-06 06:11:49 +00:00
|
|
|
return AppendCrlf(p);
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static char *AppendCache(char *p, int64_t seconds) {
|
|
|
|
struct tm tm;
|
|
|
|
if (seconds < 0) return p;
|
2020-10-06 06:11:49 +00:00
|
|
|
p = AppendHeaderName(p, "Cache-Control");
|
2021-03-25 09:21:13 +00:00
|
|
|
p = stpcpy(p, "max-age=");
|
|
|
|
p += uint64toarray_radix10(seconds, p);
|
|
|
|
if (seconds) p = stpcpy(p, ", public");
|
2020-10-06 06:11:49 +00:00
|
|
|
p = AppendCrlf(p);
|
2021-03-25 09:21:13 +00:00
|
|
|
return AppendExpires(p, (int64_t)nowish + seconds);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *AppendContentLength(char *p, size_t n) {
|
|
|
|
p = AppendHeaderName(p, "Content-Length");
|
|
|
|
p += uint64toarray_radix10(n, p);
|
|
|
|
return AppendCrlf(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *AppendContentRange(char *p, long rangestart, long rangelength,
|
|
|
|
long contentlength) {
|
|
|
|
long endrange;
|
|
|
|
CHECK_GE(rangestart + rangelength, rangestart);
|
|
|
|
CHECK_LE(rangestart + rangelength, contentlength);
|
|
|
|
if (__builtin_add_overflow(rangestart, rangelength, &endrange)) abort();
|
|
|
|
p = AppendHeaderName(p, "Content-Range");
|
2021-03-25 09:21:13 +00:00
|
|
|
p = stpcpy(p, "bytes ");
|
2020-10-06 06:11:49 +00:00
|
|
|
p += uint64toarray_radix10(rangestart, p);
|
|
|
|
*p++ = '-';
|
|
|
|
p += uint64toarray_radix10(endrange, p);
|
|
|
|
*p++ = '/';
|
|
|
|
p += uint64toarray_radix10(contentlength, p);
|
|
|
|
return AppendCrlf(p);
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static bool Inflate(uint8_t *dp, size_t dn, const uint8_t *sp, size_t sn) {
|
2020-10-06 06:11:49 +00:00
|
|
|
bool ok;
|
|
|
|
z_stream zs;
|
|
|
|
ok = false;
|
2021-03-25 09:21:13 +00:00
|
|
|
zs.next_in = sp;
|
|
|
|
zs.avail_in = sn;
|
|
|
|
zs.total_in = sn;
|
|
|
|
zs.next_out = dp;
|
|
|
|
zs.avail_out = dn;
|
|
|
|
zs.total_out = dn;
|
2020-10-06 06:11:49 +00:00
|
|
|
zs.zfree = Z_NULL;
|
|
|
|
zs.zalloc = Z_NULL;
|
|
|
|
if (inflateInit2(&zs, -MAX_WBITS) == Z_OK) {
|
|
|
|
switch (inflate(&zs, Z_NO_FLUSH)) {
|
|
|
|
case Z_STREAM_END:
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
case Z_MEM_ERROR:
|
|
|
|
WARNF("Z_MEM_ERROR");
|
|
|
|
break;
|
|
|
|
case Z_DATA_ERROR:
|
|
|
|
WARNF("Z_DATA_ERROR");
|
|
|
|
break;
|
|
|
|
case Z_NEED_DICT:
|
|
|
|
WARNF("Z_NEED_DICT");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
inflateEnd(&zs);
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
return ok;
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void *Deflate(const void *data, size_t size, size_t *out_size) {
|
|
|
|
void *res;
|
|
|
|
z_stream zs;
|
|
|
|
CHECK_EQ(Z_OK, deflateInit2(memset(&zs, 0, sizeof(zs)), 4, Z_DEFLATED,
|
|
|
|
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY));
|
|
|
|
zs.next_in = data;
|
|
|
|
zs.avail_in = size;
|
|
|
|
zs.avail_out = compressBound(size);
|
|
|
|
zs.next_out = res = xmalloc(zs.avail_out);
|
|
|
|
CHECK_EQ(Z_STREAM_END, deflate(&zs, Z_FINISH));
|
|
|
|
CHECK_EQ(Z_OK, deflateEnd(&zs));
|
|
|
|
*out_size = zs.total_out;
|
|
|
|
return xrealloc(res, zs.total_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *LoadAsset(struct Asset *a, size_t *out_size) {
|
|
|
|
size_t size;
|
|
|
|
uint8_t *data;
|
2021-03-27 14:29:55 +00:00
|
|
|
size = ZIP_LFILE_UNCOMPRESSEDSIZE(zmap + a->lf);
|
2021-03-25 09:21:13 +00:00
|
|
|
data = xmalloc(size + 1);
|
2021-03-27 14:29:55 +00:00
|
|
|
if (ZIP_LFILE_COMPRESSIONMETHOD(zmap + a->lf) == kZipCompressionDeflate) {
|
|
|
|
CHECK(Inflate(data, size, ZIP_LFILE_CONTENT(zmap + a->lf),
|
|
|
|
ZIP_LFILE_COMPRESSEDSIZE(zmap + a->lf)));
|
2020-10-06 06:11:49 +00:00
|
|
|
} else {
|
2021-03-27 14:29:55 +00:00
|
|
|
memcpy(data, ZIP_LFILE_CONTENT(zmap + a->lf), size);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
data[size] = '\0';
|
|
|
|
if (out_size) *out_size = size;
|
|
|
|
return data;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static ssize_t Send(struct iovec *iov, int iovlen) {
|
|
|
|
ssize_t rc;
|
|
|
|
if ((rc = WritevAll(client, iov, iovlen)) == -1) {
|
|
|
|
if (errno == ECONNRESET) {
|
|
|
|
DEBUGF("%s send reset", clientaddrstr);
|
|
|
|
} else {
|
|
|
|
WARNF("%s send error %s", clientaddrstr, strerror(errno));
|
|
|
|
}
|
|
|
|
connectionclose = true;
|
|
|
|
}
|
|
|
|
return rc;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
|
|
|
|
char *p;
|
|
|
|
long rangestart, rangelength;
|
|
|
|
if (IsNotModified(a)) {
|
2021-03-27 14:29:55 +00:00
|
|
|
DEBUGF("%s %s %`'.*s not modified", clientaddrstr, kHttpMethod[msg.method],
|
2021-03-25 09:21:13 +00:00
|
|
|
pathlen, path);
|
|
|
|
p = SetStatus(304, "Not Modified");
|
2021-03-22 00:27:53 +00:00
|
|
|
} else {
|
2021-03-27 14:29:55 +00:00
|
|
|
content = ZIP_LFILE_CONTENT(zmap + a->lf);
|
|
|
|
contentlength = ZIP_LFILE_COMPRESSEDSIZE(zmap + a->lf);
|
2021-03-25 09:21:13 +00:00
|
|
|
if (IsCompressed(a)) {
|
|
|
|
if (ClientAcceptsGzip()) {
|
|
|
|
gzipped = true;
|
2021-03-27 14:29:55 +00:00
|
|
|
memcpy(gzip_footer + 0, zmap + a->lf + kZipLfileOffsetCrc32, 4);
|
|
|
|
memcpy(gzip_footer + 4, zmap + a->lf + kZipLfileOffsetUncompressedsize,
|
2021-03-25 09:21:13 +00:00
|
|
|
4);
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
p = AppendHeader(p, "Content-Encoding", "gzip");
|
|
|
|
} else {
|
|
|
|
CHECK(Inflate(
|
|
|
|
(content =
|
2021-03-27 14:29:55 +00:00
|
|
|
FreeLater(xmalloc(ZIP_LFILE_UNCOMPRESSEDSIZE(zmap + a->lf)))),
|
|
|
|
(contentlength = ZIP_LFILE_UNCOMPRESSEDSIZE(zmap + a->lf)),
|
|
|
|
ZIP_LFILE_CONTENT(zmap + a->lf),
|
|
|
|
ZIP_LFILE_COMPRESSEDSIZE(zmap + a->lf)));
|
2021-03-25 09:21:13 +00:00
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
}
|
|
|
|
} else if (httpversion >= 101 && HasHeader(kHttpRange)) {
|
2021-03-27 14:29:55 +00:00
|
|
|
if (ParseHttpRange(inbuf.p + msg.headers[kHttpRange].a,
|
|
|
|
msg.headers[kHttpRange].b - msg.headers[kHttpRange].a,
|
2021-03-25 09:21:13 +00:00
|
|
|
contentlength, &rangestart, &rangelength)) {
|
|
|
|
p = SetStatus(206, "Partial Content");
|
|
|
|
p = AppendContentRange(p, rangestart, rangelength, contentlength);
|
|
|
|
content = AddRange(content, rangestart, rangelength);
|
|
|
|
contentlength = rangelength;
|
|
|
|
} else {
|
|
|
|
WARNF("%s %s %`'.*s bad range %`'.*s", clientaddrstr,
|
2021-03-27 14:29:55 +00:00
|
|
|
kHttpMethod[msg.method], pathlen, path,
|
|
|
|
msg.headers[kHttpRange].b - msg.headers[kHttpRange].a,
|
|
|
|
inbuf.p + msg.headers[kHttpRange].a);
|
2021-03-25 09:21:13 +00:00
|
|
|
p = SetStatus(416, "Range Not Satisfiable");
|
|
|
|
p = AppendContentRange(p, rangestart, rangelength, contentlength);
|
|
|
|
content = "";
|
|
|
|
contentlength = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (httpversion >= 100) {
|
|
|
|
p = AppendHeader(p, "Last-Modified", a->lastmodifiedstr);
|
|
|
|
p = AppendContentType(p, GetContentType(path, pathlen));
|
|
|
|
if (httpversion >= 101) {
|
|
|
|
p = AppendCache(p, cacheseconds);
|
|
|
|
if (!IsCompressed(a)) {
|
|
|
|
p = AppendHeader(p, "Accept-Ranges", "bytes");
|
|
|
|
} else {
|
|
|
|
p = AppendHeader(p, "Vary", "Accept-Encoding");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsValidFieldName(const char *s, size_t n) {
|
|
|
|
size_t i;
|
|
|
|
if (!n) return false;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
if (!(0x20 < s[i] && s[i] < 0x7F) || s[i] == ':') {
|
|
|
|
return false;
|
|
|
|
}
|
2021-03-22 00:27:53 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
return true;
|
2021-03-22 00:27:53 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static bool IsValidFieldContent(const char *s, size_t n) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
if (!(0x20 <= s[i] && s[i] < 0x7F)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static int LuaIsValidFieldName(lua_State *L) {
|
|
|
|
size_t size;
|
|
|
|
const char *data;
|
|
|
|
data = luaL_checklstring(L, 1, &size);
|
|
|
|
lua_pushboolean(L, IsValidFieldName(data, size));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaIsValidFieldContent(lua_State *L) {
|
|
|
|
size_t size;
|
|
|
|
const char *data;
|
|
|
|
data = luaL_checklstring(L, 1, &size);
|
|
|
|
lua_pushboolean(L, IsValidFieldContent(data, size));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static int LuaServeAsset(lua_State *L) {
|
|
|
|
size_t pathlen;
|
|
|
|
struct Asset *a;
|
|
|
|
const char *path;
|
|
|
|
path = luaL_checklstring(L, 1, &pathlen);
|
|
|
|
if (!(a = LocateAsset(path, pathlen))) {
|
|
|
|
return luaL_argerror(L, 1, "not found");
|
|
|
|
}
|
|
|
|
luaheaderp = ServeAsset(a, path, pathlen);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static int LuaRespond(lua_State *L, char *respond(int, const char *)) {
|
2021-03-25 09:21:13 +00:00
|
|
|
int code;
|
|
|
|
size_t reasonlen;
|
|
|
|
const char *reason;
|
|
|
|
code = luaL_checkinteger(L, 1);
|
|
|
|
if (!(100 <= code && code <= 999)) {
|
|
|
|
return luaL_argerror(L, 1, "bad status code");
|
|
|
|
}
|
|
|
|
if (lua_isnoneornil(L, 2)) {
|
|
|
|
reason = GetHttpReason(code);
|
|
|
|
} else {
|
|
|
|
reason = lua_tolstring(L, 2, &reasonlen);
|
|
|
|
if (reasonlen > 128 || !IsValidFieldContent(reason, reasonlen)) {
|
|
|
|
return luaL_argerror(L, 2, "invalid");
|
|
|
|
}
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
luaheaderp = respond(code, reason);
|
2021-03-25 09:21:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static int LuaSetStatus(lua_State *L) {
|
|
|
|
return LuaRespond(L, SetStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaServeError(lua_State *L) {
|
|
|
|
return LuaRespond(L, ServeError);
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static int LuaLoadAsset(lua_State *L) {
|
|
|
|
char *data;
|
|
|
|
struct Asset *a;
|
|
|
|
const char *path;
|
|
|
|
size_t size, pathlen;
|
|
|
|
path = luaL_checklstring(L, 1, &pathlen);
|
|
|
|
if (!(a = LocateAsset(path, pathlen))) {
|
|
|
|
return luaL_argerror(L, 1, "not found");
|
|
|
|
}
|
|
|
|
data = LoadAsset(a, &size);
|
|
|
|
lua_pushlstring(L, data, size);
|
|
|
|
free(data);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetDate(lua_State *L) {
|
|
|
|
lua_pushinteger(L, nowish);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetVersion(lua_State *L) {
|
|
|
|
lua_pushinteger(L, httpversion);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetMethod(lua_State *L) {
|
2021-03-27 14:29:55 +00:00
|
|
|
lua_pushstring(L, kHttpMethod[msg.method]);
|
2021-03-25 09:21:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetPath(lua_State *L) {
|
2021-03-27 14:29:55 +00:00
|
|
|
lua_pushlstring(L, request.path.p, request.path.n);
|
2021-03-25 09:21:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetFragment(lua_State *L) {
|
2021-03-27 14:29:55 +00:00
|
|
|
lua_pushlstring(L, request.fragment.p, request.fragment.n);
|
2021-03-25 09:21:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetUri(lua_State *L) {
|
2021-03-27 14:29:55 +00:00
|
|
|
lua_pushlstring(L, inbuf.p + msg.uri.a, msg.uri.b - msg.uri.a);
|
2021-03-25 09:21:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaFormatDate(lua_State *L) {
|
|
|
|
int64_t t;
|
|
|
|
char buf[30];
|
|
|
|
struct tm tm;
|
|
|
|
t = luaL_checkinteger(L, 1);
|
|
|
|
gmtime_r(&t, &tm);
|
|
|
|
lua_pushstring(L, FormatHttpDateTime(buf, &tm));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaParseDate(lua_State *L) {
|
|
|
|
size_t n;
|
|
|
|
const char *s;
|
|
|
|
s = luaL_checklstring(L, 1, &n);
|
|
|
|
lua_pushinteger(L, ParseHttpDateTime(s, n));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetClientAddr(lua_State *L) {
|
|
|
|
lua_pushstring(L, clientaddrstr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetServerAddr(lua_State *L) {
|
|
|
|
lua_pushstring(L, serveraddrstr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static int LuaGetPayload(lua_State *L) {
|
|
|
|
lua_pushlstring(L, inbuf.p + hdrsize, msgsize - hdrsize);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static int LuaGetHeader(lua_State *L) {
|
|
|
|
int h;
|
|
|
|
const char *key, *val;
|
2021-03-27 14:29:55 +00:00
|
|
|
size_t i, keylen, vallen;
|
2021-03-25 09:21:13 +00:00
|
|
|
key = luaL_checklstring(L, 1, &keylen);
|
|
|
|
if ((h = GetHttpHeader(key, keylen)) != -1) {
|
2021-03-27 14:29:55 +00:00
|
|
|
val = inbuf.p + msg.headers[h].a;
|
|
|
|
vallen = msg.headers[h].b - msg.headers[h].a;
|
|
|
|
lua_pushlstring(L, val, vallen);
|
2021-03-25 09:21:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
for (i = 0; i < msg.xheaders.n; ++i) {
|
|
|
|
if (!CompareSlicesCase(key, keylen, inbuf.p + msg.xheaders.p[i].k.a,
|
|
|
|
msg.xheaders.p[i].k.b - msg.xheaders.p[i].k.a)) {
|
|
|
|
lua_pushlstring(L, inbuf.p + msg.xheaders.p[i].v.a,
|
|
|
|
msg.xheaders.p[i].v.b - msg.xheaders.p[i].v.a);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_pushstring(L, "");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetHeaders(lua_State *L) {
|
|
|
|
size_t i;
|
|
|
|
lua_newtable(L);
|
|
|
|
for (i = 0; i < kHttpHeadersMax; ++i) {
|
|
|
|
if (msg.headers[i].b - msg.headers[i].a) {
|
|
|
|
lua_pushlstring(L, inbuf.p + msg.headers[i].a,
|
|
|
|
msg.headers[i].b - msg.headers[i].a);
|
|
|
|
lua_setfield(L, -2, GetHttpHeaderName(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < msg.xheaders.n; ++i) {
|
|
|
|
lua_pushlstring(L, inbuf.p + msg.xheaders.p[i].v.a,
|
|
|
|
msg.xheaders.p[i].v.b - msg.xheaders.p[i].v.a);
|
|
|
|
lua_setfield(
|
|
|
|
L, -2,
|
|
|
|
FreeLater(strndup(inbuf.p + msg.xheaders.p[i].k.a,
|
|
|
|
msg.xheaders.p[i].k.b - msg.xheaders.p[i].k.a)));
|
|
|
|
}
|
|
|
|
return 1;
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaSetHeader(lua_State *L) {
|
|
|
|
char *p;
|
|
|
|
ssize_t rc;
|
|
|
|
const char *key, *val;
|
|
|
|
size_t i, keylen, vallen;
|
|
|
|
key = luaL_checklstring(L, 1, &keylen);
|
|
|
|
val = luaL_checklstring(L, 2, &vallen);
|
|
|
|
if (!IsValidFieldName(key, keylen)) {
|
|
|
|
return luaL_argerror(L, 1, "invalid");
|
|
|
|
}
|
|
|
|
if (!IsValidFieldContent(val, vallen)) {
|
|
|
|
return luaL_argerror(L, 2, "invalid");
|
|
|
|
}
|
|
|
|
if (!luaheaderp) {
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
} else {
|
|
|
|
while (luaheaderp - hdrbuf.p + keylen + 2 + vallen + 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;
|
|
|
|
}
|
|
|
|
switch (GetHttpHeader(key, keylen)) {
|
|
|
|
case kHttpDate:
|
|
|
|
case kHttpContentRange:
|
|
|
|
case kHttpContentLength:
|
|
|
|
case kHttpContentEncoding:
|
|
|
|
return luaL_argerror(L, 1, "abstracted");
|
|
|
|
case kHttpConnection:
|
|
|
|
if (vallen != 5 || memcmp(val, "close", 5)) {
|
|
|
|
return luaL_argerror(L, 2, "unsupported");
|
|
|
|
}
|
|
|
|
connectionclose = true;
|
|
|
|
break;
|
|
|
|
case kHttpContentType:
|
|
|
|
p = AppendContentType(p, val);
|
|
|
|
break;
|
|
|
|
case kHttpServer:
|
|
|
|
branded = true;
|
|
|
|
p = AppendHeader(p, key, val);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
p = AppendHeader(p, key, val);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
luaheaderp = p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaHasParam(lua_State *L) {
|
|
|
|
const char *key;
|
|
|
|
size_t i, keylen;
|
|
|
|
key = luaL_checklstring(L, 1, &keylen);
|
2021-03-27 14:29:55 +00:00
|
|
|
for (i = 0; i < request.params.n; ++i) {
|
|
|
|
if (request.params.p[i].key.n == keylen &&
|
|
|
|
!memcmp(request.params.p[i].key.p, key, keylen)) {
|
2021-03-25 09:21:13 +00:00
|
|
|
lua_pushboolean(L, true);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_pushboolean(L, false);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaGetParam(lua_State *L) {
|
|
|
|
const char *key;
|
|
|
|
size_t i, keylen;
|
|
|
|
key = luaL_checklstring(L, 1, &keylen);
|
2021-03-27 14:29:55 +00:00
|
|
|
for (i = 0; i < request.params.n; ++i) {
|
|
|
|
if (request.params.p[i].key.n == keylen &&
|
|
|
|
!memcmp(request.params.p[i].key.p, key, keylen)) {
|
|
|
|
if (request.params.p[i].val.n == SIZE_MAX) break;
|
|
|
|
lua_pushlstring(L, request.params.p[i].val.p, request.params.p[i].val.n);
|
2021-03-25 09:21:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_pushnil(L);
|
2021-03-22 00:27:53 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static int LuaGetParams(lua_State *L) {
|
|
|
|
size_t i;
|
|
|
|
lua_newtable(L);
|
2021-03-27 14:29:55 +00:00
|
|
|
for (i = 0; i < request.params.n; ++i) {
|
2021-03-25 09:21:13 +00:00
|
|
|
lua_newtable(L);
|
2021-03-27 14:29:55 +00:00
|
|
|
lua_pushlstring(L, request.params.p[i].key.p, request.params.p[i].key.n);
|
2021-03-25 09:21:13 +00:00
|
|
|
lua_seti(L, -2, 1);
|
2021-03-27 14:29:55 +00:00
|
|
|
if (request.params.p[i].val.n != SIZE_MAX) {
|
|
|
|
lua_pushlstring(L, request.params.p[i].val.p, request.params.p[i].val.n);
|
2021-03-25 09:21:13 +00:00
|
|
|
lua_seti(L, -2, 2);
|
|
|
|
}
|
|
|
|
lua_seti(L, -2, i + 1);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaWrite(lua_State *L) {
|
|
|
|
int h;
|
2021-03-22 00:27:53 +00:00
|
|
|
size_t size;
|
|
|
|
const char *data;
|
|
|
|
data = luaL_checklstring(L, 1, &size);
|
2021-03-25 09:21:13 +00:00
|
|
|
outbuf.p = xrealloc(outbuf.p, outbuf.n + size);
|
|
|
|
memcpy(outbuf.p + outbuf.n, data, size);
|
|
|
|
outbuf.n += size;
|
2021-03-22 00:27:53 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static int LuaEscaper(lua_State *L,
|
|
|
|
struct EscapeResult escape(const char *, size_t)) {
|
|
|
|
size_t size;
|
|
|
|
const char *data;
|
|
|
|
struct EscapeResult r;
|
|
|
|
data = luaL_checklstring(L, 1, &size);
|
|
|
|
r = escape(data, size);
|
|
|
|
lua_pushlstring(L, r.data, r.size);
|
|
|
|
free(r.data);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaEscapeHtml(lua_State *L) {
|
|
|
|
return LuaEscaper(L, EscapeHtml);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaEscapeParam(lua_State *L) {
|
|
|
|
return LuaEscaper(L, EscapeUrlParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaEscapePath(lua_State *L) {
|
|
|
|
return LuaEscaper(L, EscapeUrlPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaEscapeSegment(lua_State *L) {
|
|
|
|
return LuaEscaper(L, EscapeUrlPathSegment);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaEscapeFragment(lua_State *L) {
|
|
|
|
return LuaEscaper(L, EscapeUrlFragment);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaEscapeLiteral(lua_State *L) {
|
|
|
|
return LuaEscaper(L, EscapeJsStringLiteral);
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static int LuaPopcnt(lua_State *L) {
|
|
|
|
lua_pushinteger(L, popcnt(luaL_checkinteger(L, 1)));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaBsr(lua_State *L) {
|
|
|
|
long x;
|
|
|
|
if ((x = luaL_checkinteger(L, 1))) {
|
|
|
|
lua_pushinteger(L, bsr(x));
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return luaL_argerror(L, 1, "zero");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LuaBsf(lua_State *L) {
|
|
|
|
long x;
|
|
|
|
if ((x = luaL_checkinteger(L, 1))) {
|
|
|
|
lua_pushinteger(L, bsf(x));
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return luaL_argerror(L, 1, "zero");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 05:31:41 +00:00
|
|
|
static void LuaRun(const char *path) {
|
|
|
|
struct Asset *a;
|
|
|
|
const char *code;
|
|
|
|
if ((a = LocateAsset(path, strlen(path)))) {
|
|
|
|
code = LoadAsset(a, NULL);
|
|
|
|
if (luaL_dostring(L, code) != LUA_OK) {
|
|
|
|
WARNF("%s %s", path, lua_tostring(L, -1));
|
|
|
|
}
|
|
|
|
free(code);
|
|
|
|
} else {
|
|
|
|
DEBUGF("%s not found", path);
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
|
2021-03-22 00:27:53 +00:00
|
|
|
static const luaL_Reg kLuaFuncs[] = {
|
2021-03-27 14:29:55 +00:00
|
|
|
{"EscapeFragment", LuaEscapeFragment}, //
|
|
|
|
{"EscapeHtml", LuaEscapeHtml}, //
|
|
|
|
{"EscapeLiteral", LuaEscapeLiteral}, //
|
|
|
|
{"EscapeParam", LuaEscapeParam}, //
|
|
|
|
{"EscapePath", LuaEscapePath}, //
|
|
|
|
{"EscapeSegment", LuaEscapeSegment}, //
|
|
|
|
{"FormatDate", LuaFormatDate}, //
|
|
|
|
{"GetClientAddr", LuaGetClientAddr}, //
|
|
|
|
{"GetDate", LuaGetDate}, //
|
|
|
|
{"GetFragment", LuaGetFragment}, //
|
|
|
|
{"GetHeader", LuaGetHeader}, //
|
|
|
|
{"GetHeaders", LuaGetHeaders}, //
|
|
|
|
{"GetMethod", LuaGetMethod}, //
|
|
|
|
{"GetParam", LuaGetParam}, //
|
|
|
|
{"GetParams", LuaGetParams}, //
|
|
|
|
{"GetPath", LuaGetPath}, //
|
|
|
|
{"GetPayload", LuaGetPayload}, //
|
|
|
|
{"GetServerAddr", LuaGetServerAddr}, //
|
|
|
|
{"GetUri", LuaGetUri}, //
|
|
|
|
{"GetVersion", LuaGetVersion}, //
|
|
|
|
{"HasParam", LuaHasParam}, //
|
|
|
|
{"IsValidFieldContent", LuaIsValidFieldContent}, //
|
|
|
|
{"IsValidFieldName", LuaIsValidFieldName}, //
|
|
|
|
{"LoadAsset", LuaLoadAsset}, //
|
|
|
|
{"ParseDate", LuaParseDate}, //
|
|
|
|
{"ServeAsset", LuaServeAsset}, //
|
|
|
|
{"ServeError", LuaServeError}, //
|
|
|
|
{"SetHeader", LuaSetHeader}, //
|
|
|
|
{"SetStatus", LuaSetStatus}, //
|
|
|
|
{"Write", LuaWrite}, //
|
|
|
|
{"bsf", LuaBsf}, //
|
|
|
|
{"bsr", LuaBsr}, //
|
|
|
|
{"popcnt", LuaPopcnt}, //
|
2021-03-22 00:27:53 +00:00
|
|
|
};
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void LuaSetArgv(void) {
|
|
|
|
size_t i;
|
|
|
|
lua_newtable(L);
|
|
|
|
for (i = optind; i < __argc; ++i) {
|
|
|
|
lua_pushstring(L, __argv[i]);
|
|
|
|
lua_seti(L, -2, i - optind + 1);
|
|
|
|
}
|
|
|
|
lua_setglobal(L, "argv");
|
|
|
|
}
|
|
|
|
|
2021-03-22 00:27:53 +00:00
|
|
|
static void LuaInit(void) {
|
|
|
|
size_t i;
|
|
|
|
L = luaL_newstate();
|
2021-03-27 05:31:41 +00:00
|
|
|
luaL_openlibs(L);
|
2021-03-22 00:27:53 +00:00
|
|
|
for (i = 0; i < ARRAYLEN(kLuaFuncs); ++i) {
|
|
|
|
lua_pushcfunction(L, kLuaFuncs[i].func);
|
|
|
|
lua_setglobal(L, kLuaFuncs[i].name);
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
LuaSetArgv();
|
2021-03-27 05:31:41 +00:00
|
|
|
LuaRun("/tool/net/.init.lua");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LuaReload(void) {
|
|
|
|
LuaRun("/tool/net/.reload.lua");
|
2021-03-22 00:27:53 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static char *ServeLua(struct Asset *a) {
|
|
|
|
char *p;
|
|
|
|
outbuf.n = 0;
|
|
|
|
luaheaderp = NULL;
|
|
|
|
if (luaL_dostring(L, FreeLater(LoadAsset(a, NULL))) == LUA_OK) {
|
|
|
|
if (!(p = luaheaderp)) {
|
|
|
|
p = SetStatus(200, "OK");
|
|
|
|
p = AppendContentType(p, "text/html");
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
if (outbuf.n) {
|
|
|
|
if (istext && outbuf.n >= 100 && ClientAcceptsGzip()) {
|
|
|
|
gzipped = true;
|
|
|
|
p = AppendHeader(p, "Content-Encoding", "gzip");
|
|
|
|
p = AppendHeader(p, "Vary", "Accept-Encoding");
|
|
|
|
WRITE32LE(gzip_footer + 0, crc32_z(0, outbuf.p, outbuf.n));
|
|
|
|
WRITE32LE(gzip_footer + 4, outbuf.n);
|
|
|
|
content = FreeLater(Deflate(outbuf.p, outbuf.n, &contentlength));
|
|
|
|
} else {
|
|
|
|
content = outbuf.p;
|
|
|
|
contentlength = outbuf.n;
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
return p;
|
|
|
|
} else {
|
2021-03-22 00:27:53 +00:00
|
|
|
WARNF("%s %s", clientaddrstr, lua_tostring(L, -1));
|
|
|
|
lua_pop(L, 1); /* remove message */
|
2021-03-25 09:21:13 +00:00
|
|
|
connectionclose = true;
|
|
|
|
return ServeError(500, "Internal Server Error");
|
2021-03-22 00:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static bool IsLua(struct Asset *a) {
|
2021-03-27 14:29:55 +00:00
|
|
|
return ZIP_LFILE_NAMESIZE(zmap + a->lf) >= 4 &&
|
|
|
|
!memcmp(ZIP_LFILE_NAME(zmap + a->lf) +
|
|
|
|
ZIP_LFILE_NAMESIZE(zmap + a->lf) - 4,
|
2021-03-25 09:21:13 +00:00
|
|
|
".lua", 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) {
|
|
|
|
char *p;
|
|
|
|
if (IsLua(a)) {
|
|
|
|
p = ServeLua(a);
|
2021-03-27 14:29:55 +00:00
|
|
|
} else if (msg.method == kHttpGet || msg.method == kHttpHead) {
|
2021-03-25 09:21:13 +00:00
|
|
|
p = ServeAsset(a, path, pathlen);
|
|
|
|
p = AppendHeader(p, "X-Content-Type-Options", "nosniff");
|
|
|
|
} else {
|
|
|
|
p = ServeError(405, "Method Not Allowed");
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *HandleRedirect(struct Redirect *r) {
|
2020-10-06 06:11:49 +00:00
|
|
|
char *p;
|
|
|
|
struct Asset *a;
|
2021-03-25 09:21:13 +00:00
|
|
|
if ((a = LocateAsset(r->location, strlen(r->location)))) {
|
|
|
|
DEBUGF("%s %s %`'.*s rewritten %`'s", clientaddrstr,
|
2021-03-27 14:29:55 +00:00
|
|
|
kHttpMethod[msg.method], request.path.n, request.path.p,
|
|
|
|
r->location);
|
2021-03-25 09:21:13 +00:00
|
|
|
p = HandleAsset(a, r->location, strlen(r->location));
|
|
|
|
} else if (httpversion == 9) {
|
|
|
|
p = ServeError(505, "HTTP Version Not Supported");
|
|
|
|
} else {
|
|
|
|
DEBUGF("%s %s %`'.*s redirecting %`'s", clientaddrstr,
|
2021-03-27 14:29:55 +00:00
|
|
|
kHttpMethod[msg.method], request.path.n, request.path.p,
|
|
|
|
r->location);
|
2021-03-25 09:21:13 +00:00
|
|
|
p = SetStatus(307, "Temporary Redirect");
|
|
|
|
p = AppendHeader(p, "Location", r->location);
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static void LogMessage(const char *d, const char *s, size_t n) {
|
|
|
|
if (!logmessages) return;
|
|
|
|
while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n;
|
|
|
|
LOGF("%s %s %,ld byte message\n%.*s", clientaddrstr, d, n, n, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t SendMessageString(const char *s) {
|
|
|
|
size_t n;
|
2021-03-27 05:31:41 +00:00
|
|
|
ssize_t rc;
|
2021-03-27 14:29:55 +00:00
|
|
|
n = strlen(s);
|
|
|
|
LogMessage("sending", s, n);
|
|
|
|
return 0;
|
2021-03-27 05:31:41 +00:00
|
|
|
for (;;) {
|
2021-03-27 14:29:55 +00:00
|
|
|
if ((rc = write(client, s, n)) != -1 || errno != EINTR) {
|
2021-03-27 05:31:41 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t SendContinue(void) {
|
2021-03-27 14:29:55 +00:00
|
|
|
return SendMessageString("\
|
2021-03-27 05:31:41 +00:00
|
|
|
HTTP/1.1 100 Continue\r\n\
|
|
|
|
\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t SendTimeout(void) {
|
2021-03-27 14:29:55 +00:00
|
|
|
return SendMessageString("\
|
2021-03-27 05:31:41 +00:00
|
|
|
HTTP/1.1 408 Request Timeout\r\n\
|
|
|
|
Connection: close\r\n\
|
|
|
|
Content-Length: 0\r\n\
|
|
|
|
\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t SendServiceUnavailable(void) {
|
2021-03-27 14:29:55 +00:00
|
|
|
return SendMessageString("\
|
2021-03-27 05:31:41 +00:00
|
|
|
HTTP/1.1 503 Service Unavailable\r\n\
|
|
|
|
Connection: close\r\n\
|
|
|
|
Content-Length: 0\r\n\
|
|
|
|
\r\n");
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void LogClose(const char *reason) {
|
|
|
|
if (amtread) {
|
2021-03-27 14:29:55 +00:00
|
|
|
WARNF("%s %s with %,ld bytes unprocessed", clientaddrstr, reason);
|
2021-03-25 09:21:13 +00:00
|
|
|
} else {
|
|
|
|
DEBUGF("%s %s", clientaddrstr, reason);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 05:31:41 +00:00
|
|
|
static const char *DescribeClose(void) {
|
|
|
|
if (killed) return "killed";
|
|
|
|
if (meltdown) return "meltdown";
|
|
|
|
if (terminated) return "terminated";
|
|
|
|
if (connectionclose) return "connectionclose";
|
|
|
|
return "destroyed";
|
|
|
|
}
|
|
|
|
|
2021-03-27 14:29:55 +00:00
|
|
|
static char *HandleMessage(void) {
|
2021-03-25 09:21:13 +00:00
|
|
|
long r;
|
|
|
|
ssize_t cl, rc;
|
|
|
|
struct Asset *a;
|
|
|
|
size_t got, need;
|
|
|
|
httpversion =
|
2021-03-27 14:29:55 +00:00
|
|
|
ParseHttpVersion(inbuf.p + msg.version.a, msg.version.b - msg.version.a);
|
2021-03-25 09:21:13 +00:00
|
|
|
if (httpversion > 101) {
|
|
|
|
return ServeError(505, "HTTP Version Not Supported");
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
if (msg.method > kHttpPost ||
|
2021-03-25 09:21:13 +00:00
|
|
|
(HasHeader(kHttpTransferEncoding) &&
|
2021-03-27 14:29:55 +00:00
|
|
|
!HeaderEquals(kHttpTransferEncoding, "identity"))) {
|
2021-03-25 09:21:13 +00:00
|
|
|
return ServeError(501, "Not Implemented");
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
if (HasHeader(kHttpExpect) && !HeaderEquals(kHttpExpect, "100-continue")) {
|
2021-03-27 05:31:41 +00:00
|
|
|
return ServeError(417, "Expectation Failed");
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
if ((cl = ParseContentLength(inbuf.p + msg.headers[kHttpContentLength].a,
|
|
|
|
msg.headers[kHttpContentLength].b -
|
|
|
|
msg.headers[kHttpContentLength].a)) == -1) {
|
2021-03-25 09:21:13 +00:00
|
|
|
if (HasHeader(kHttpContentLength)) {
|
|
|
|
return ServeError(400, "Bad Request");
|
2021-03-27 14:29:55 +00:00
|
|
|
} else if (msg.method != kHttpGet && msg.method != kHttpHead &&
|
|
|
|
msg.method != kHttpOptions) {
|
2021-03-25 09:21:13 +00:00
|
|
|
return ServeError(411, "Length Required");
|
|
|
|
} else {
|
|
|
|
cl = 0;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
need = hdrsize + cl; /* synchronization is possible */
|
2021-03-25 09:21:13 +00:00
|
|
|
if (need > inbuf.n) {
|
|
|
|
return ServeError(413, "Payload Too Large");
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
if (HeaderEquals(kHttpExpect, "100-continue") && httpversion >= 101) {
|
2021-03-27 05:31:41 +00:00
|
|
|
SendContinue();
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
while (amtread < need) {
|
|
|
|
if (++frags == 64) {
|
|
|
|
LogClose("payload fragged");
|
|
|
|
return ServeError(408, "Request Timeout");
|
|
|
|
}
|
|
|
|
if ((rc = read(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
|
|
|
|
if (!(got = rc)) {
|
|
|
|
LogClose("payload disconnect");
|
|
|
|
return ServeError(400, "Bad Request");
|
|
|
|
}
|
|
|
|
amtread += got;
|
|
|
|
} else if (errno == ECONNRESET) {
|
|
|
|
LogClose("payload reset");
|
|
|
|
return ServeError(400, "Bad Request");
|
|
|
|
} else if (errno == EINTR) {
|
|
|
|
if (killed || ((meltdown || terminated) && nowl() - startread > 1)) {
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
return ServeError(503, "Service Unavailable");
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
} else {
|
2021-03-25 09:21:13 +00:00
|
|
|
WARNF("%s payload recv %s", clientaddrstr, strerror(errno));
|
|
|
|
return ServeError(500, "Internal Server Error");
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
msgsize = need; /* we are now synchronized */
|
|
|
|
if (httpversion != 101 || IsConnectionClose()) {
|
|
|
|
connectionclose = true;
|
|
|
|
}
|
|
|
|
if (!ParseRequestUri()) {
|
2021-03-27 14:29:55 +00:00
|
|
|
WARNF("%s could not parse request request %`'.*s", clientaddrstr,
|
|
|
|
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a);
|
2021-03-25 09:21:13 +00:00
|
|
|
connectionclose = true;
|
|
|
|
return ServeError(400, "Bad Request");
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
if (HeaderEquals(kHttpContentType, "application/x-www-form-urlencoded")) {
|
|
|
|
ParseFormParams();
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
VERBOSEF("%s %s %`'.*s referrer %`'.*s", clientaddrstr,
|
2021-03-27 14:29:55 +00:00
|
|
|
kHttpMethod[msg.method], request.path.n, request.path.p,
|
|
|
|
msg.headers[kHttpReferer].b - msg.headers[kHttpReferer].a,
|
|
|
|
inbuf.p + msg.headers[kHttpReferer].a);
|
|
|
|
if ((a = LocateAsset(request.path.p, request.path.n))) {
|
|
|
|
return HandleAsset(a, request.path.p, request.path.n);
|
|
|
|
} else if ((r = FindRedirect(request.path.p, request.path.n)) != -1) {
|
2021-03-25 09:21:13 +00:00
|
|
|
return HandleRedirect(redirects.p + r);
|
2020-09-07 04:39:00 +00:00
|
|
|
} else {
|
2021-03-25 09:21:13 +00:00
|
|
|
return ServeError(404, "Not Found");
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool HandleRequest(void) {
|
|
|
|
int rc;
|
|
|
|
char *p;
|
|
|
|
int iovlen;
|
|
|
|
struct iovec iov[4];
|
|
|
|
long actualcontentlength;
|
2021-03-27 14:29:55 +00:00
|
|
|
if ((rc = ParseHttpRequest(&msg, inbuf.p, amtread)) != -1) {
|
2021-03-25 09:21:13 +00:00
|
|
|
if (!rc) return false;
|
2021-03-27 14:29:55 +00:00
|
|
|
hdrsize = rc;
|
|
|
|
LogMessage("received", inbuf.p, hdrsize);
|
|
|
|
p = HandleMessage();
|
2021-03-25 09:21:13 +00:00
|
|
|
} else {
|
|
|
|
httpversion = 101;
|
|
|
|
p = ServeError(400, "Bad Request");
|
|
|
|
}
|
|
|
|
if (!msgsize) {
|
|
|
|
amtread = 0;
|
|
|
|
connectionclose = true;
|
|
|
|
DEBUGF("%s could not synchronize message stream", clientaddrstr);
|
|
|
|
} else if (msgsize < amtread) {
|
|
|
|
DEBUGF("%s has %,ld pipelined bytes", clientaddrstr, amtread - msgsize);
|
|
|
|
memmove(inbuf.p, inbuf.p + msgsize, amtread - msgsize);
|
|
|
|
amtread -= msgsize;
|
|
|
|
} else {
|
|
|
|
amtread = 0;
|
|
|
|
}
|
|
|
|
if (httpversion >= 100) {
|
|
|
|
p = AppendHeader(p, "Date", currentdate);
|
|
|
|
if (!branded) {
|
|
|
|
p = AppendHeader(p, "Server", serverheader);
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
if (connectionclose) {
|
|
|
|
p = AppendHeader(p, "Connection", "close");
|
|
|
|
}
|
|
|
|
actualcontentlength = contentlength;
|
2020-10-06 06:11:49 +00:00
|
|
|
if (gzipped) {
|
2021-03-25 09:21:13 +00:00
|
|
|
actualcontentlength += sizeof(kGzipHeader) + sizeof(gzip_footer);
|
|
|
|
}
|
|
|
|
p = AppendContentLength(p, actualcontentlength);
|
|
|
|
p = AppendCrlf(p);
|
|
|
|
CHECK_LE(p - hdrbuf.p, hdrbuf.n);
|
2021-03-27 14:29:55 +00:00
|
|
|
LogMessage("sending", hdrbuf.p, p - hdrbuf.p);
|
2021-03-25 09:21:13 +00:00
|
|
|
iov[0].iov_base = hdrbuf.p;
|
|
|
|
iov[0].iov_len = p - hdrbuf.p;
|
|
|
|
iovlen = 1;
|
|
|
|
if (!MustNotIncludeMessageBody()) {
|
|
|
|
if (gzipped) {
|
|
|
|
iov[iovlen].iov_base = kGzipHeader;
|
|
|
|
iov[iovlen].iov_len = sizeof(kGzipHeader);
|
|
|
|
++iovlen;
|
|
|
|
}
|
|
|
|
iov[iovlen].iov_base = content;
|
|
|
|
iov[iovlen].iov_len = contentlength;
|
2020-10-06 06:11:49 +00:00
|
|
|
++iovlen;
|
2021-03-25 09:21:13 +00:00
|
|
|
if (gzipped) {
|
|
|
|
iov[iovlen].iov_base = gzip_footer;
|
|
|
|
iov[iovlen].iov_len = sizeof(gzip_footer);
|
|
|
|
++iovlen;
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
} else {
|
|
|
|
iov[0].iov_base = content;
|
|
|
|
iov[0].iov_len = contentlength;
|
|
|
|
iovlen = 1;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
Send(iov, iovlen);
|
|
|
|
CollectGarbage();
|
|
|
|
return true;
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void InitRequest(void) {
|
|
|
|
frags = 0;
|
2021-03-27 14:29:55 +00:00
|
|
|
msgsize = 0;
|
2021-03-25 09:21:13 +00:00
|
|
|
content = NULL;
|
|
|
|
gzipped = false;
|
|
|
|
branded = false;
|
|
|
|
contentlength = 0;
|
2021-03-27 14:29:55 +00:00
|
|
|
InitHttpRequest(&msg);
|
2021-03-25 09:21:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ProcessRequests(void) {
|
2020-10-06 06:11:49 +00:00
|
|
|
ssize_t rc;
|
2021-03-25 09:21:13 +00:00
|
|
|
size_t got;
|
2020-10-06 06:11:49 +00:00
|
|
|
long double now;
|
2021-03-25 09:21:13 +00:00
|
|
|
for (;;) {
|
|
|
|
InitRequest();
|
|
|
|
startread = nowl();
|
|
|
|
for (;;) {
|
2021-03-27 14:29:55 +00:00
|
|
|
if (!msg.i && amtread && HandleRequest()) break;
|
2021-03-25 09:21:13 +00:00
|
|
|
if ((rc = read(client, inbuf.p + amtread, inbuf.n - amtread)) != -1) {
|
|
|
|
startrequest = now = nowl();
|
|
|
|
if (now - nowish > 1) UpdateCurrentDate(now);
|
|
|
|
got = rc;
|
|
|
|
amtread += got;
|
|
|
|
if (amtread) {
|
|
|
|
if (HandleRequest()) {
|
|
|
|
break;
|
|
|
|
} else if (got) {
|
|
|
|
if (++frags == 32) {
|
|
|
|
SendTimeout();
|
|
|
|
LogClose("fragged");
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
DEBUGF("%s fragmented msg %,ld %,ld", clientaddrstr, amtread,
|
|
|
|
got);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!got) {
|
|
|
|
LogClose("disconnect");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (errno == ECONNRESET) {
|
|
|
|
LogClose("reset");
|
|
|
|
return;
|
|
|
|
} else if (errno != EINTR) {
|
|
|
|
WARNF("%s recv msg %s", clientaddrstr, strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (killed || (terminated && !amtread) ||
|
|
|
|
(meltdown && (!amtread || nowl() - startread > 1))) {
|
|
|
|
if (amtread) SendServiceUnavailable();
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (connectionclose || killed || ((terminated || meltdown) && !amtread)) {
|
|
|
|
LogClose(DescribeClose());
|
|
|
|
return;
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 09:21:13 +00:00
|
|
|
static void ProcessConnection(void) {
|
2020-10-06 06:11:49 +00:00
|
|
|
int pid;
|
|
|
|
clientaddrsize = sizeof(clientaddr);
|
2021-03-27 14:29:55 +00:00
|
|
|
if ((client = accept4(server, &clientaddr, &clientaddrsize, SOCK_CLOEXEC)) !=
|
|
|
|
-1) {
|
2020-10-06 06:11:49 +00:00
|
|
|
startconnection = nowl();
|
2021-03-25 09:21:13 +00:00
|
|
|
if (uniprocess) {
|
|
|
|
pid = -1;
|
|
|
|
connectionclose = true;
|
|
|
|
} else {
|
|
|
|
switch ((pid = fork())) {
|
|
|
|
case 0:
|
|
|
|
meltdown = false;
|
|
|
|
connectionclose = false;
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
WARNF("redbean is entering meltdown mode");
|
|
|
|
LOGIFNEG1(kill(0, SIGUSR2));
|
|
|
|
SendServiceUnavailable();
|
|
|
|
/* fallthrough */
|
|
|
|
default:
|
|
|
|
close(client);
|
|
|
|
return;
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
DescribeAddress(clientaddrstr, &clientaddr);
|
|
|
|
DEBUGF("%s accept", clientaddrstr);
|
|
|
|
ProcessRequests();
|
|
|
|
LOGIFNEG1(close(client));
|
|
|
|
if (!pid) _exit(0);
|
|
|
|
} else if (errno != EINTR) {
|
|
|
|
FATALF("%s accept error %s", serveraddrstr, strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-11 04:18:53 +00:00
|
|
|
static void TuneServerSocket(void) {
|
2020-10-06 06:11:49 +00:00
|
|
|
int yes = 1;
|
|
|
|
LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)));
|
|
|
|
LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)));
|
2020-10-11 04:18:53 +00:00
|
|
|
LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)));
|
|
|
|
LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_FASTOPEN, &yes, sizeof(yes)));
|
|
|
|
LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_QUICKACK, &yes, sizeof(yes)));
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RedBean(void) {
|
2021-02-27 18:47:19 +00:00
|
|
|
uint32_t addrsize;
|
2021-03-02 07:45:49 +00:00
|
|
|
if (IsWindows()) uniprocess = true;
|
2021-03-02 08:38:11 +00:00
|
|
|
if (daemonize) Daemonize();
|
2020-10-06 06:11:49 +00:00
|
|
|
gmtoff = GetGmtOffset();
|
2021-03-27 14:29:55 +00:00
|
|
|
OpenZip((const char *)getauxval(AT_EXECFN));
|
|
|
|
IndexAssets();
|
2021-03-25 09:21:13 +00:00
|
|
|
xsigaction(SIGINT, OnInt, 0, 0, 0);
|
|
|
|
xsigaction(SIGHUP, OnHup, 0, 0, 0);
|
|
|
|
xsigaction(SIGTERM, OnTerm, 0, 0, 0);
|
|
|
|
xsigaction(SIGCHLD, OnChld, 0, 0, 0);
|
|
|
|
xsigaction(SIGUSR1, OnUsr1, 0, 0, 0);
|
|
|
|
xsigaction(SIGUSR2, OnUsr2, 0, 0, 0);
|
|
|
|
xsigaction(SIGALRM, OnAlrm, 0, 0, 0);
|
2020-10-06 06:11:49 +00:00
|
|
|
xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0);
|
2021-03-25 09:21:13 +00:00
|
|
|
if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) {
|
|
|
|
heartless = true;
|
|
|
|
}
|
2021-03-27 14:29:55 +00:00
|
|
|
CHECK_NE(-1,
|
|
|
|
(server = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP)));
|
2020-10-11 04:18:53 +00:00
|
|
|
TuneServerSocket();
|
2021-03-07 19:31:44 +00:00
|
|
|
if (bind(server, &serveraddr, sizeof(serveraddr)) == -1) {
|
|
|
|
if (errno == EADDRINUSE) {
|
|
|
|
fprintf(stderr, "error: address in use\n"
|
|
|
|
"try passing the -p PORT flag\n");
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "error: bind() failed: %s\n", strerror(errno));
|
|
|
|
}
|
|
|
|
exit(1);
|
|
|
|
}
|
2020-09-07 04:39:00 +00:00
|
|
|
CHECK_NE(-1, listen(server, 10));
|
2021-02-27 18:47:19 +00:00
|
|
|
addrsize = sizeof(serveraddr);
|
|
|
|
CHECK_NE(-1, getsockname(server, &serveraddr, &addrsize));
|
2020-09-07 04:39:00 +00:00
|
|
|
DescribeAddress(serveraddrstr, &serveraddr);
|
2020-10-06 06:11:49 +00:00
|
|
|
VERBOSEF("%s listen", serveraddrstr);
|
2021-02-27 18:47:19 +00:00
|
|
|
if (printport) {
|
|
|
|
printf("%d\n", ntohs(serveraddr.sin_port));
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
2021-03-25 09:21:13 +00:00
|
|
|
UpdateCurrentDate(nowl());
|
|
|
|
inbuf.n = 64 * 1024;
|
|
|
|
inbuf.p = xvalloc(inbuf.n);
|
|
|
|
hdrbuf.n = 4 * 1024;
|
|
|
|
hdrbuf.p = xvalloc(hdrbuf.n);
|
2021-03-27 05:31:41 +00:00
|
|
|
LuaInit();
|
2020-09-07 04:39:00 +00:00
|
|
|
while (!terminated) {
|
2021-03-25 09:21:13 +00:00
|
|
|
if (zombied) {
|
|
|
|
ReapZombies();
|
|
|
|
} else if (invalidated) {
|
2021-03-27 05:31:41 +00:00
|
|
|
LuaReload();
|
2020-10-06 06:11:49 +00:00
|
|
|
invalidated = false;
|
2021-03-25 09:21:13 +00:00
|
|
|
} else if (heartbeat) {
|
2020-10-06 06:11:49 +00:00
|
|
|
UpdateCurrentDate(nowl());
|
|
|
|
heartbeat = false;
|
2021-03-25 09:21:13 +00:00
|
|
|
} else {
|
|
|
|
if (heartless) {
|
|
|
|
UpdateCurrentDate(nowl());
|
|
|
|
}
|
|
|
|
ProcessConnection();
|
2020-10-06 06:11:49 +00:00
|
|
|
}
|
2020-09-07 04:39:00 +00:00
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
VERBOSEF("%s terminated", serveraddrstr);
|
2020-09-07 04:39:00 +00:00
|
|
|
LOGIFNEG1(close(server));
|
2021-03-25 09:21:13 +00:00
|
|
|
if (!keyboardinterrupt) {
|
|
|
|
if (!killed) {
|
|
|
|
terminated = false;
|
|
|
|
}
|
|
|
|
LOGIFNEG1(kill(0, SIGTERM));
|
|
|
|
}
|
2020-10-06 06:11:49 +00:00
|
|
|
WaitAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
showcrashreports();
|
2021-03-25 09:21:13 +00:00
|
|
|
SetDefaults();
|
2020-10-06 06:11:49 +00:00
|
|
|
GetOpts(argc, argv);
|
|
|
|
RedBean();
|
2020-09-07 04:39:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|