mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 15:03:34 +00:00
Add preliminary lua support to redbean
This change only implements enough Lua support to send a Hello World response. The redbean executable size increases from ~128kb to 260kb and the requests per second decreases from 1000k to 600k. That's the fastest it can go and that's extremely impressive compared to Python See #97
This commit is contained in:
parent
2f41b4dcc6
commit
6b90ff60cd
3 changed files with 135 additions and 77 deletions
|
@ -40,6 +40,7 @@ TOOL_NET_DIRECTDEPS = \
|
||||||
LIBC_X \
|
LIBC_X \
|
||||||
NET_HTTP \
|
NET_HTTP \
|
||||||
THIRD_PARTY_GETOPT \
|
THIRD_PARTY_GETOPT \
|
||||||
|
THIRD_PARTY_LUA \
|
||||||
THIRD_PARTY_ZLIB \
|
THIRD_PARTY_ZLIB \
|
||||||
TOOL_DECODE_LIB
|
TOOL_DECODE_LIB
|
||||||
|
|
||||||
|
@ -65,18 +66,7 @@ o/$(MODE)/tool/net/redbean.com.dbg: \
|
||||||
o/$(MODE)/tool/net/redbean.png.zip.o \
|
o/$(MODE)/tool/net/redbean.png.zip.o \
|
||||||
o/$(MODE)/tool/net/redbean.css.zip.o \
|
o/$(MODE)/tool/net/redbean.css.zip.o \
|
||||||
o/$(MODE)/tool/net/redbean.html.zip.o \
|
o/$(MODE)/tool/net/redbean.html.zip.o \
|
||||||
o/$(MODE)/tool/net/net.pkg \
|
o/$(MODE)/tool/net/redbean.lua.zip.o \
|
||||||
$(CRT) \
|
|
||||||
$(APE)
|
|
||||||
@$(APELINK)
|
|
||||||
|
|
||||||
o/$(MODE)/tool/net/greenbean.com.dbg: \
|
|
||||||
$(TOOL_NET_DEPS) \
|
|
||||||
o/$(MODE)/tool/net/greenbean.o \
|
|
||||||
o/$(MODE)/tool/net/redbean.ico.zip.o \
|
|
||||||
o/$(MODE)/tool/net/redbean.png.zip.o \
|
|
||||||
o/$(MODE)/tool/net/redbean.css.zip.o \
|
|
||||||
o/$(MODE)/tool/net/redbean.html.zip.o \
|
|
||||||
o/$(MODE)/tool/net/net.pkg \
|
o/$(MODE)/tool/net/net.pkg \
|
||||||
$(CRT) \
|
$(CRT) \
|
||||||
$(APE)
|
$(APE)
|
||||||
|
|
|
@ -71,6 +71,10 @@
|
||||||
#include "libc/zipos/zipos.internal.h"
|
#include "libc/zipos/zipos.internal.h"
|
||||||
#include "net/http/http.h"
|
#include "net/http/http.h"
|
||||||
#include "third_party/getopt/getopt.h"
|
#include "third_party/getopt/getopt.h"
|
||||||
|
#include "third_party/lua/lauxlib.h"
|
||||||
|
#include "third_party/lua/ltests.h"
|
||||||
|
#include "third_party/lua/lua.h"
|
||||||
|
#include "third_party/lua/lualib.h"
|
||||||
#include "third_party/zlib/zlib.h"
|
#include "third_party/zlib/zlib.h"
|
||||||
|
|
||||||
/* TODO(jart): Implement more lenient message framing */
|
/* TODO(jart): Implement more lenient message framing */
|
||||||
|
@ -249,6 +253,7 @@ static const char *logpath;
|
||||||
static int64_t programtime;
|
static int64_t programtime;
|
||||||
static const char *programfile;
|
static const char *programfile;
|
||||||
static const char *serverheader;
|
static const char *serverheader;
|
||||||
|
static lua_State *L;
|
||||||
|
|
||||||
static long double nowish;
|
static long double nowish;
|
||||||
static long double startrequest;
|
static long double startrequest;
|
||||||
|
@ -888,7 +893,7 @@ static bool InflateZlib(uint8_t *outbuf, size_t outsize, const uint8_t *inbuf,
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Inflate(uint8_t *outbuf, size_t outsize, const uint8_t *inbuf,
|
static bool Inflate(void *outbuf, size_t outsize, const uint8_t *inbuf,
|
||||||
size_t insize) {
|
size_t insize) {
|
||||||
if (IsTiny()) {
|
if (IsTiny()) {
|
||||||
return InflateTiny(outbuf, outsize, inbuf, insize);
|
return InflateTiny(outbuf, outsize, inbuf, insize);
|
||||||
|
@ -904,6 +909,56 @@ static void LogRequestLatency(void) {
|
||||||
(long)((now - startconnection) * 1e9));
|
(long)((now - startconnection) * 1e9));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static nodiscard char *LoadAssetAsString(struct Asset *a) {
|
||||||
|
char *code;
|
||||||
|
code = xmalloc(ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf) + 1);
|
||||||
|
if (ZIP_CFILE_COMPRESSIONMETHOD(zbase + a->cf) == kZipCompressionDeflate) {
|
||||||
|
CHECK(Inflate(code, ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf),
|
||||||
|
ZIP_LFILE_CONTENT(zbase + ZIP_CFILE_OFFSET(zbase + a->cf)),
|
||||||
|
ZIP_CFILE_COMPRESSEDSIZE(zbase + a->cf)));
|
||||||
|
} else {
|
||||||
|
memcpy(code, ZIP_LFILE_CONTENT(zbase + ZIP_CFILE_OFFSET(zbase + a->cf)),
|
||||||
|
ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf));
|
||||||
|
}
|
||||||
|
code[ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf)] = '\0';
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int LuaDate(lua_State *L) {
|
||||||
|
lua_pushstring(L, currentdate);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int LuaSend(lua_State *L) {
|
||||||
|
size_t size;
|
||||||
|
const char *data;
|
||||||
|
data = luaL_checklstring(L, 1, &size);
|
||||||
|
WritevAll(client, &(struct iovec){data, size}, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const luaL_Reg kLuaFuncs[] = {
|
||||||
|
{"date", LuaDate},
|
||||||
|
{"send", LuaSend},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void LuaInit(void) {
|
||||||
|
size_t i;
|
||||||
|
L = luaL_newstate();
|
||||||
|
for (i = 0; i < ARRAYLEN(kLuaFuncs); ++i) {
|
||||||
|
lua_pushcfunction(L, kLuaFuncs[i].func);
|
||||||
|
lua_setglobal(L, kLuaFuncs[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LuaRun(struct Asset *a) {
|
||||||
|
if (luaL_dostring(L, gc(LoadAssetAsString(a))) != LUA_OK) {
|
||||||
|
WARNF("%s %s", clientaddrstr, lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1); /* remove message */
|
||||||
|
terminated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HandleRequest(size_t got) {
|
void HandleRequest(size_t got) {
|
||||||
char *p;
|
char *p;
|
||||||
int iovlen;
|
int iovlen;
|
||||||
|
@ -939,73 +994,78 @@ void HandleRequest(size_t got) {
|
||||||
if ((location = LookupRedirect(path, pathlen))) {
|
if ((location = LookupRedirect(path, pathlen))) {
|
||||||
p = AppendRedirect(p, location);
|
p = AppendRedirect(p, location);
|
||||||
} else if ((a = FindFile(path, pathlen))) {
|
} else if ((a = FindFile(path, pathlen))) {
|
||||||
if (IsNotModified(a)) {
|
if (pathlen >= 4 && !memcmp(path + pathlen - 4, ".lua", 4)) {
|
||||||
VERBOSEF("%s %s %.*s not modified", clientaddrstr,
|
LuaRun(a);
|
||||||
kHttpMethod[req.method], pathlen, path);
|
return;
|
||||||
p = AppendStatus(p, 304, "Not Modified");
|
|
||||||
} else {
|
} else {
|
||||||
lf = ZIP_CFILE_OFFSET(zbase + a->cf);
|
if (IsNotModified(a)) {
|
||||||
content = ZIP_LFILE_CONTENT(zbase + lf);
|
VERBOSEF("%s %s %.*s not modified", clientaddrstr,
|
||||||
contentlength = ZIP_CFILE_COMPRESSEDSIZE(zbase + a->cf);
|
kHttpMethod[req.method], pathlen, path);
|
||||||
if (IsCompressed(a)) {
|
p = AppendStatus(p, 304, "Not Modified");
|
||||||
if (memmem(inbuf + req.headers[kHttpAcceptEncoding].a,
|
|
||||||
req.headers[kHttpAcceptEncoding].b -
|
|
||||||
req.headers[kHttpAcceptEncoding].a,
|
|
||||||
"gzip", 4)) {
|
|
||||||
gzipped = true;
|
|
||||||
memcpy(gzip_footer + 0, zbase + a->cf + kZipCfileOffsetCrc32,
|
|
||||||
4);
|
|
||||||
memcpy(gzip_footer + 4,
|
|
||||||
zbase + a->cf + kZipCfileOffsetUncompressedsize, 4);
|
|
||||||
p = AppendStatus(p, 200, "OK");
|
|
||||||
p = AppendContentEncodingGzip(p);
|
|
||||||
} else if (Inflate(
|
|
||||||
(content = gc(xmalloc(
|
|
||||||
ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf)))),
|
|
||||||
(contentlength =
|
|
||||||
ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf)),
|
|
||||||
ZIP_LFILE_CONTENT(zbase + lf),
|
|
||||||
ZIP_CFILE_COMPRESSEDSIZE(zbase + a->cf))) {
|
|
||||||
p = AppendStatus(p, 200, "OK");
|
|
||||||
} else {
|
|
||||||
WARNF("%s %s %.*s internal server error", clientaddrstr,
|
|
||||||
kHttpMethod[req.method], pathlen, path);
|
|
||||||
p = AppendStatus(p, 500, "Internal Server Error");
|
|
||||||
content = "Internal Server Error\r\n";
|
|
||||||
contentlength = -1;
|
|
||||||
}
|
|
||||||
} else if (HasHeader(kHttpRange)) {
|
|
||||||
if (ParseHttpRange(
|
|
||||||
inbuf + req.headers[kHttpRange].a,
|
|
||||||
req.headers[kHttpRange].b - req.headers[kHttpRange].a,
|
|
||||||
contentlength, &rangestart, &rangelength)) {
|
|
||||||
p = AppendStatus(p, 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,
|
|
||||||
kHttpMethod[req.method], pathlen, path,
|
|
||||||
req.headers[kHttpRange].b - req.headers[kHttpRange].a,
|
|
||||||
inbuf + req.headers[kHttpRange].a);
|
|
||||||
p = AppendStatus(p, 416, "Range Not Satisfiable");
|
|
||||||
p = AppendContentRange(p, rangestart, rangelength,
|
|
||||||
contentlength);
|
|
||||||
content = "";
|
|
||||||
contentlength = 0;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
p = AppendStatus(p, 200, "OK");
|
lf = ZIP_CFILE_OFFSET(zbase + a->cf);
|
||||||
|
content = ZIP_LFILE_CONTENT(zbase + lf);
|
||||||
|
contentlength = ZIP_CFILE_COMPRESSEDSIZE(zbase + a->cf);
|
||||||
|
if (IsCompressed(a)) {
|
||||||
|
if (memmem(inbuf + req.headers[kHttpAcceptEncoding].a,
|
||||||
|
req.headers[kHttpAcceptEncoding].b -
|
||||||
|
req.headers[kHttpAcceptEncoding].a,
|
||||||
|
"gzip", 4)) {
|
||||||
|
gzipped = true;
|
||||||
|
memcpy(gzip_footer + 0, zbase + a->cf + kZipCfileOffsetCrc32,
|
||||||
|
4);
|
||||||
|
memcpy(gzip_footer + 4,
|
||||||
|
zbase + a->cf + kZipCfileOffsetUncompressedsize, 4);
|
||||||
|
p = AppendStatus(p, 200, "OK");
|
||||||
|
p = AppendContentEncodingGzip(p);
|
||||||
|
} else if (Inflate(
|
||||||
|
(content = gc(xmalloc(ZIP_CFILE_UNCOMPRESSEDSIZE(
|
||||||
|
zbase + a->cf)))),
|
||||||
|
(contentlength =
|
||||||
|
ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf)),
|
||||||
|
ZIP_LFILE_CONTENT(zbase + lf),
|
||||||
|
ZIP_CFILE_COMPRESSEDSIZE(zbase + a->cf))) {
|
||||||
|
p = AppendStatus(p, 200, "OK");
|
||||||
|
} else {
|
||||||
|
WARNF("%s %s %.*s internal server error", clientaddrstr,
|
||||||
|
kHttpMethod[req.method], pathlen, path);
|
||||||
|
p = AppendStatus(p, 500, "Internal Server Error");
|
||||||
|
content = "Internal Server Error\r\n";
|
||||||
|
contentlength = -1;
|
||||||
|
}
|
||||||
|
} else if (HasHeader(kHttpRange)) {
|
||||||
|
if (ParseHttpRange(
|
||||||
|
inbuf + req.headers[kHttpRange].a,
|
||||||
|
req.headers[kHttpRange].b - req.headers[kHttpRange].a,
|
||||||
|
contentlength, &rangestart, &rangelength)) {
|
||||||
|
p = AppendStatus(p, 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,
|
||||||
|
kHttpMethod[req.method], pathlen, path,
|
||||||
|
req.headers[kHttpRange].b - req.headers[kHttpRange].a,
|
||||||
|
inbuf + req.headers[kHttpRange].a);
|
||||||
|
p = AppendStatus(p, 416, "Range Not Satisfiable");
|
||||||
|
p = AppendContentRange(p, rangestart, rangelength,
|
||||||
|
contentlength);
|
||||||
|
content = "";
|
||||||
|
contentlength = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p = AppendStatus(p, 200, "OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = AppendLastModified(p, a->lastmodifiedstr);
|
||||||
|
p = AppendContentType(p, GetContentType(path, pathlen));
|
||||||
|
p = AppendCache(p);
|
||||||
|
if (!IsCompressed(a)) {
|
||||||
|
p = AppendAcceptRangesBytes(p);
|
||||||
|
} else {
|
||||||
|
p = AppendVaryContentEncoding(p);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
p = AppendLastModified(p, a->lastmodifiedstr);
|
|
||||||
p = AppendContentType(p, GetContentType(path, pathlen));
|
|
||||||
p = AppendCache(p);
|
|
||||||
if (!IsCompressed(a)) {
|
|
||||||
p = AppendAcceptRangesBytes(p);
|
|
||||||
} else {
|
|
||||||
p = AppendVaryContentEncoding(p);
|
|
||||||
}
|
}
|
||||||
} else if (!strncmp(path, "/", pathlen) ||
|
} else if (!strncmp(path, "/", pathlen) ||
|
||||||
!strncmp(path, "/index.html", pathlen)) {
|
!strncmp(path, "/index.html", pathlen)) {
|
||||||
|
@ -1175,6 +1235,7 @@ void RedBean(void) {
|
||||||
printf("%d\n", ntohs(serveraddr.sin_port));
|
printf("%d\n", ntohs(serveraddr.sin_port));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
LuaInit();
|
||||||
heartbeat = true;
|
heartbeat = true;
|
||||||
while (!terminated) {
|
while (!terminated) {
|
||||||
if (invalidated) {
|
if (invalidated) {
|
||||||
|
|
7
tool/net/redbean.lua
Normal file
7
tool/net/redbean.lua
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
send('HTTP/1.1 200 OK\r\n'..
|
||||||
|
'Date: ' .. date() .. '\r\n'..
|
||||||
|
'Server: redbean/0.1\r\n'..
|
||||||
|
'Content-Type: text/plain\r\n'..
|
||||||
|
'Content-Length: 7\r\n'..
|
||||||
|
'\r\n'..
|
||||||
|
'hello\r\n')
|
Loading…
Reference in a new issue