Enable serving zip directory entries

This commit is contained in:
Justine Tunney 2021-03-29 02:39:20 -07:00
parent 1753b669cf
commit 7dc8973567
3 changed files with 131 additions and 50 deletions

View file

@ -1,2 +1,3 @@
-- special script called by main redbean process at startup -- special script called by main redbean process at startup
ProgramRedirect(0, '/favicon.ico', '/tool/net/redbean.ico') ProgramRedirect(0, '/favicon.ico', '/tool/net/redbean.ico')
HidePath('/usr/share/zoneinfo/')

View file

@ -36,6 +36,7 @@ TOOL_NET_DIRECTDEPS = \
LIBC_SYSV \ LIBC_SYSV \
LIBC_SYSV_CALLS \ LIBC_SYSV_CALLS \
LIBC_TIME \ LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \ LIBC_UNICODE \
LIBC_X \ LIBC_X \
NET_HTTP \ NET_HTTP \

View file

@ -345,7 +345,6 @@ static size_t hdrsize;
static size_t msgsize; static size_t msgsize;
static size_t amtread; static size_t amtread;
static char *luaheaderp; static char *luaheaderp;
struct Strings stagedirs;
static const char *sauce; static const char *sauce;
static const char *brand; static const char *brand;
static const char *pidpath; static const char *pidpath;
@ -355,6 +354,8 @@ static size_t contentlength;
static int64_t cacheseconds; static int64_t cacheseconds;
static uint8_t gzip_footer[8]; static uint8_t gzip_footer[8];
static const char *serverheader; static const char *serverheader;
static struct Strings stagedirs;
static struct Strings hidepaths;
static struct Buffer logo; static struct Buffer logo;
static struct Buffer inbuf; static struct Buffer inbuf;
@ -508,7 +509,7 @@ static const char *FindContentType(uint64_t ext) {
return NULL; return NULL;
} }
static const char *GetContentType(const char *path, size_t n) { static const char *GetContentType2(const char *path, size_t n) {
size_t i; size_t i;
uint64_t x; uint64_t x;
const char *p, *r; const char *p, *r;
@ -521,7 +522,15 @@ static const char *GetContentType(const char *path, size_t n) {
return r; return r;
} }
} }
return "application/octet-stream"; return NULL;
}
static const char *GetContentType(struct Asset *a, const char *path, size_t n) {
return firstnonnull(
GetContentType2(path, n),
firstnonnull(GetContentType2(ZIP_LFILE_NAME(zmap + a->lf),
ZIP_LFILE_NAMESIZE(zmap + a->lf)),
"application/octet-stream"));
} }
static void DescribeAddress(char buf[32], const struct sockaddr_in *addr) { static void DescribeAddress(char buf[32], const struct sockaddr_in *addr) {
@ -575,6 +584,11 @@ static char *RemoveTrailingSlashes(char *s) {
return s; return s;
} }
static void AddString(struct Strings *l, char *s) {
l->p = xrealloc(l->p, ++l->n * sizeof(*l->p));
l->p[l->n - 1] = s;
}
static void AddStagingDirectory(const char *dirpath) { static void AddStagingDirectory(const char *dirpath) {
char *s; char *s;
s = RemoveTrailingSlashes(strdup(dirpath)); s = RemoveTrailingSlashes(strdup(dirpath));
@ -582,8 +596,7 @@ static void AddStagingDirectory(const char *dirpath) {
fprintf(stderr, "error: not a directory: %s\n", s); fprintf(stderr, "error: not a directory: %s\n", s);
exit(1); exit(1);
} }
stagedirs.p = xrealloc(stagedirs.p, ++stagedirs.n * sizeof(*stagedirs.p)); AddString(&stagedirs, s);
stagedirs.p[stagedirs.n - 1] = s;
} }
static void GetOpts(int argc, char *argv[]) { static void GetOpts(int argc, char *argv[]) {
@ -884,6 +897,11 @@ static void IndexAssets(void) {
ZIP_CFILE_NAMESIZE(zmap + cf), ZIP_CFILE_NAME(zmap + cf)); ZIP_CFILE_NAMESIZE(zmap + cf), ZIP_CFILE_NAME(zmap + cf));
continue; continue;
} }
if (ZIP_CFILE_NAMESIZE(zmap + cf) > 1 &&
ZIP_CFILE_NAME(zmap + cf)[ZIP_CFILE_NAMESIZE(zmap + cf) - 1] == '/' &&
!ZIP_CFILE_UNCOMPRESSEDSIZE(zmap + cf)) {
continue;
}
hash = Hash(ZIP_CFILE_NAME(zmap + cf), ZIP_CFILE_NAMESIZE(zmap + cf)); hash = Hash(ZIP_CFILE_NAME(zmap + cf), ZIP_CFILE_NAMESIZE(zmap + cf));
step = 0; step = 0;
do { do {
@ -932,19 +950,22 @@ static struct Asset *FindAsset(const char *path, size_t pathlen) {
} }
static struct Asset *LocateAssetZip(const char *path, size_t pathlen) { static struct Asset *LocateAssetZip(const char *path, size_t pathlen) {
char *path2; char *p2, *p3, *p4;
struct Asset *a; struct Asset *a;
if (pathlen && path[0] == '/') ++path, --pathlen; if (pathlen && path[0] == '/') ++path, --pathlen;
if (!(a = FindAsset(path, pathlen)) && if (!(a = FindAsset(path, pathlen)) &&
(!pathlen || (pathlen && path[pathlen - 1] == '/'))) { (!pathlen || (pathlen && path[pathlen - 1] == '/'))) {
path2 = xmalloc(pathlen + 10); p2 = strndup(path, pathlen);
memcpy(path2, path, pathlen); p3 = xjoinpaths(p2, "index.lua");
memcpy(path2 + pathlen, "index.lua", 9); LOGF("find asset %s", p3);
if (!(a = FindAsset(path2, pathlen + 9))) { if (!(a = FindAsset(p3, strlen(p3)))) {
memcpy(path2 + pathlen, "index.html", 10); p4 = xjoinpaths(p2, "index.html");
a = FindAsset(path2, pathlen + 10); LOGF("find asset %s", p4);
a = FindAsset(p4, strlen(p4));
free(p4);
} }
free(path2); free(p3);
free(p2);
} }
return a; return a;
} }
@ -1360,7 +1381,7 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
} }
if (httpversion >= 100) { if (httpversion >= 100) {
p = AppendHeader(p, "Last-Modified", a->lastmodifiedstr); p = AppendHeader(p, "Last-Modified", a->lastmodifiedstr);
p = AppendContentType(p, GetContentType(path, pathlen)); p = AppendContentType(p, GetContentType(a, path, pathlen));
if (httpversion >= 101) { if (httpversion >= 101) {
p = AppendCache(p, cacheseconds); p = AppendCache(p, cacheseconds);
if (!IsCompressed(a)) { if (!IsCompressed(a)) {
@ -1410,6 +1431,30 @@ static char *CommitOutput(char *p) {
return p; return p;
} }
static char *GetAssetPath(uint32_t cf, size_t *out_size) {
char *p1, *p2;
size_t n1, n2;
p1 = ZIP_CFILE_NAME(zmap + cf);
n1 = ZIP_CFILE_NAMESIZE(zmap + cf);
p2 = malloc(1 + n1 + 1);
n2 = 1 + n1 + 1;
p2[0] = '/';
memcpy(p2 + 1, p1, n1);
p2[1 + n1] = '\0';
if (out_size) *out_size = 1 + n1;
return p2;
}
static bool IsHiddenPath(const char *s) {
size_t i;
for (i = 0; i < hidepaths.n; ++i) {
if (startswith(s, hidepaths.p[i])) {
return true;
}
}
return false;
}
static int LuaServeAsset(lua_State *L) { static int LuaServeAsset(lua_State *L) {
size_t pathlen; size_t pathlen;
struct Asset *a; struct Asset *a;
@ -1821,6 +1866,11 @@ static int LuaSetLogLevel(lua_State *L) {
return 0; return 0;
} }
static int LuaHidePath(lua_State *L) {
AddString(&hidepaths, strdup(luaL_checkstring(L, 1)));
return 0;
}
static int LuaLog(lua_State *L) { static int LuaLog(lua_State *L) {
int level; int level;
lua_Debug ar; lua_Debug ar;
@ -1836,6 +1886,29 @@ static int LuaLog(lua_State *L) {
return 0; return 0;
} }
static int LuaIsHiddenPath(lua_State *L) {
lua_pushboolean(L, IsHiddenPath(luaL_checkstring(L, 1)));
return 1;
}
static int LuaGetZipPaths(lua_State *L) {
char *path;
uint32_t cf;
size_t i, n, pathlen;
lua_newtable(L);
i = 0;
n = ZIP_CDIR_RECORDS(zdir);
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));
path = GetAssetPath(cf, &pathlen);
lua_pushlstring(L, path, pathlen);
lua_seti(L, -2, ++i);
free(path);
}
return 1;
}
static void LuaRun(const char *path) { static void LuaRun(const char *path) {
struct Asset *a; struct Asset *a;
const char *code; const char *code;
@ -1874,7 +1947,9 @@ static const luaL_Reg kLuaFuncs[] = {
{"GetServerAddr", LuaGetServerAddr}, // {"GetServerAddr", LuaGetServerAddr}, //
{"GetUri", LuaGetUri}, // {"GetUri", LuaGetUri}, //
{"GetVersion", LuaGetVersion}, // {"GetVersion", LuaGetVersion}, //
{"GetZipPaths", LuaGetZipPaths}, //
{"HasParam", LuaHasParam}, // {"HasParam", LuaHasParam}, //
{"HidePath", LuaHidePath}, //
{"LoadAsset", LuaLoadAsset}, // {"LoadAsset", LuaLoadAsset}, //
{"Log", LuaLog}, // {"Log", LuaLog}, //
{"ParseHttpDateTime", LuaParseHttpDateTime}, // {"ParseHttpDateTime", LuaParseHttpDateTime}, //
@ -2108,10 +2183,10 @@ static char *ServeListing(void) {
char tb[64]; char tb[64];
int w, x, y; int w, x, y;
struct tm tm; struct tm tm;
char *p, *path;
int64_t lastmod; int64_t lastmod;
uint32_t cf, lf; uint32_t cf, lf;
size_t n, n1, n2; size_t i, n, pathlen;
char *p, *p1, *p2;
struct EscapeResult r[3]; struct EscapeResult r[3];
AppendString("\ AppendString("\
<!doctype html>\n\ <!doctype html>\n\
@ -2128,8 +2203,11 @@ a {\n\
img {\n\ img {\n\
vertical-align: middle;\n\ vertical-align: middle;\n\
}\n\ }\n\
footer {\n\
font-size: 11pt;\n\
}\n\
</style>\n\ </style>\n\
<h1>\n"); <header><h1>\n");
if (logo.n) { if (logo.n) {
AppendString("<img src=\"data:image/png;base64,"); AppendString("<img src=\"data:image/png;base64,");
AppendData(logo.p, logo.n); AppendData(logo.p, logo.n);
@ -2138,35 +2216,34 @@ img {\n\
r[0] = EscapeHtml(brand, strlen(brand)); r[0] = EscapeHtml(brand, strlen(brand));
AppendData(r[0].data, r[0].size); AppendData(r[0].data, r[0].size);
free(r[0].data); free(r[0].data);
AppendString("</h1><hr><pre>\n"); AppendString("</h1><hr></header><pre>\n");
w = x = 0; w = x = 0;
n = ZIP_CDIR_RECORDS(zdir); n = ZIP_CDIR_RECORDS(zdir);
CHECK_EQ(kZipCdirHdrMagic, ZIP_CDIR_MAGIC(zdir)); CHECK_EQ(kZipCdirHdrMagic, ZIP_CDIR_MAGIC(zdir));
for (cf = ZIP_CDIR_OFFSET(zdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) { for (cf = ZIP_CDIR_OFFSET(zdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf)); CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
y = strnwidth(ZIP_CFILE_NAME(zmap + cf), ZIP_CFILE_NAMESIZE(zmap + cf), 0); if (!IsHiddenPath((path = GetAssetPath(cf, &pathlen)))) {
y = strwidth(path, 0);
w = MIN(80, MAX(w, y + 2)); w = MIN(80, MAX(w, y + 2));
y = ZIP_CFILE_UNCOMPRESSEDSIZE(zmap + cf); y = ZIP_CFILE_UNCOMPRESSEDSIZE(zmap + cf);
y = y ? llog10(y) : 1; y = y ? llog10(y) : 1;
x = MIN(80, MAX(x, y + (y - 1) / 3 + 2)); x = MIN(80, MAX(x, y + (y - 1) / 3 + 2));
} }
}
n = ZIP_CDIR_RECORDS(zdir); n = ZIP_CDIR_RECORDS(zdir);
for (cf = ZIP_CDIR_OFFSET(zdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) { for (cf = ZIP_CDIR_OFFSET(zdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf)); CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
p1 = ZIP_CFILE_NAME(zmap + cf); path = GetAssetPath(cf, &pathlen);
n1 = ZIP_CFILE_NAMESIZE(zmap + cf); if (!IsHiddenPath(path)) {
p2 = malloc(1 + n1); r[0] = EscapeHtml(path, pathlen);
n2 = 1 + n1; r[1] = EscapeUrlPath(path, pathlen);
*p2 = '/';
memcpy(p2 + 1, p1, n1);
r[0] = EscapeHtml(p2, n2);
r[1] = EscapeUrlPath(p2, n2);
r[2] = EscapeHtml(r[1].data, r[1].size); r[2] = EscapeHtml(r[1].data, r[1].size);
lastmod = GetLastModifiedZip(zmap + cf); lastmod = GetLastModifiedZip(zmap + cf);
localtime_r(&lastmod, &tm); localtime_r(&lastmod, &tm);
strftime(tb, sizeof(tb), "%Y-%m-%d %H:%M:%S", &tm); strftime(tb, sizeof(tb), "%Y-%m-%d %H:%M:%S", &tm);
if (IsCompressionMethodSupported(ZIP_CFILE_COMPRESSIONMETHOD(zmap + cf)) && if (IsCompressionMethodSupported(
IsAcceptableHttpRequestPath(p2, n2)) { ZIP_CFILE_COMPRESSIONMETHOD(zmap + cf)) &&
IsAcceptableHttpRequestPath(path, pathlen)) {
AppendFmt("<a href=\"%.*s\">%-*.*s</a> %s %4s %,*ld\n", r[2].size, AppendFmt("<a href=\"%.*s\">%-*.*s</a> %s %4s %,*ld\n", r[2].size,
r[2].data, w, r[0].size, r[0].data, tb, r[2].data, w, r[0].size, r[0].data, tb,
DescribeCompressionRatio(rb, cf), x, DescribeCompressionRatio(rb, cf), x,
@ -2179,9 +2256,10 @@ img {\n\
free(r[2].data); free(r[2].data);
free(r[1].data); free(r[1].data);
free(r[0].data); free(r[0].data);
free(p2);
} }
AppendString("</pre><hr><p>\n"); free(path);
}
AppendString("</pre><footer><hr><p>\n");
if (!unbranded) { if (!unbranded) {
AppendString("\ AppendString("\
this listing is for /\n\ this listing is for /\n\
@ -2197,6 +2275,7 @@ your redbean is ");
} }
w = shared->workers; w = shared->workers;
AppendFmt("currently servicing %,d connection%s\n", w, w == 1 ? "" : "s"); AppendFmt("currently servicing %,d connection%s\n", w, w == 1 ? "" : "s");
AppendString("</footer>\n");
p = SetStatus(200, "OK"); p = SetStatus(200, "OK");
p = AppendCache(p, 0); p = AppendCache(p, 0);
return CommitOutput(p); return CommitOutput(p);