@ -34,6 +34,7 @@
# include "libc/log/log.h"
# include "libc/log/log.h"
# include "libc/macros.internal.h"
# include "libc/macros.internal.h"
# include "libc/math.h"
# include "libc/math.h"
# include "libc/mem/fmt.h"
# include "libc/mem/mem.h"
# include "libc/mem/mem.h"
# include "libc/nexgen32e/bsf.h"
# include "libc/nexgen32e/bsf.h"
# include "libc/nexgen32e/bsr.h"
# include "libc/nexgen32e/bsr.h"
@ -99,16 +100,18 @@ FLAGS\n\
-z print port\n\
-z print port\n\
-m log messages\n\
-m log messages\n\
-b log message bodies\n\
-b log message bodies\n\
-k encourage keep-alive\n\
-D DIR serve assets from directory\n\
-D DIR serve assets from directory\n\
-c INT cache seconds\n\
-c INT cache seconds\n\
-r / X= / Y redirect X to Y\n\
-r / X= / Y redirect X to Y\n\
-R / X= / Y rewrite X to Y\n\
-l ADDR listen ip [default 0.0.0.0]\n\
-l ADDR listen ip [default 0.0.0.0]\n\
-p PORT listen port [default 8080]\n\
-p PORT listen port [default 8080]\n\
-L PATH log file location\n\
-L PATH log file location\n\
-P PATH pid file location\n\
-P PATH pid file location\n\
-U INT daemon set user id\n\
-U INT daemon set user id\n\
-G INT daemon set group id\n\
-G INT daemon set group id\n\
-B STR changes server header \n\
-B STR changes brand \n\
\n\
\n\
FEATURES\n\
FEATURES\n\
\n\
\n\
@ -149,10 +152,6 @@ USAGE\n\
connection processes, which grow to whatever number your system\n\
connection processes, which grow to whatever number your system\n\
limits and tcp stack configuration allow. If fork() should fail\n\
limits and tcp stack configuration allow. If fork() should fail\n\
then redbean starts shutting idle connections down.\n\
then redbean starts shutting idle connections down.\n\
\n\
Redirects emit a 307 response unless the location exists in\n\
the zip directory, in which case it transparently rewrites.\n\
\n\
\n"
\n"
# define HASH_LOAD_FACTOR /* 1. / */ 4
# define HASH_LOAD_FACTOR /* 1. / */ 4
@ -281,6 +280,7 @@ static struct Freelist {
static struct Redirects {
static struct Redirects {
size_t n ;
size_t n ;
struct Redirect {
struct Redirect {
int code ;
const char * path ;
const char * path ;
size_t pathlen ;
size_t pathlen ;
const char * location ;
const char * location ;
@ -301,12 +301,17 @@ static struct Assets {
} * p ;
} * p ;
} assets ;
} assets ;
static struct Shared {
int workers ; //
} * shared ;
static bool killed ;
static bool killed ;
static bool istext ;
static bool istext ;
static bool zombied ;
static bool zombied ;
static bool gzipped ;
static bool gzipped ;
static bool branded ;
static bool branded ;
static bool meltdown ;
static bool meltdown ;
static bool unbranded ;
static bool heartless ;
static bool heartless ;
static bool printport ;
static bool printport ;
static bool heartbeat ;
static bool heartbeat ;
@ -319,6 +324,7 @@ static bool logmessages;
static bool checkedmethod ;
static bool checkedmethod ;
static bool connectionclose ;
static bool connectionclose ;
static bool keyboardinterrupt ;
static bool keyboardinterrupt ;
static bool encouragekeepalive ;
static int frags ;
static int frags ;
static int gmtoff ;
static int gmtoff ;
@ -340,6 +346,8 @@ static size_t msgsize;
static size_t amtread ;
static size_t amtread ;
static char * luaheaderp ;
static char * luaheaderp ;
struct Strings stagedirs ;
struct Strings stagedirs ;
static const char * sauce ;
static const char * brand ;
static const char * pidpath ;
static const char * pidpath ;
static const char * logpath ;
static const char * logpath ;
static int64_t programtime ;
static int64_t programtime ;
@ -348,6 +356,7 @@ 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 Buffer logo ;
static struct Buffer inbuf ;
static struct Buffer inbuf ;
static struct Buffer hdrbuf ;
static struct Buffer hdrbuf ;
static struct Buffer outbuf ;
static struct Buffer outbuf ;
@ -355,6 +364,7 @@ static struct Request request;
static long double nowish ;
static long double nowish ;
static long double startread ;
static long double startread ;
static long double lastmeltdown ;
static long double startrequest ;
static long double startrequest ;
static long double startconnection ;
static long double startconnection ;
static struct sockaddr_in serveraddr ;
static struct sockaddr_in serveraddr ;
@ -436,15 +446,17 @@ static long FindRedirect(const char *path, size_t n) {
return - 1 ;
return - 1 ;
}
}
static void Add Redirect( const char * arg ) {
static void Program Redirect( int code , const char * src , const char * dst ) {
long i , j ;
long i , j ;
const char * p ;
struct Redirect r ;
struct Redirect r ;
CHECK_NOTNULL ( ( p = strchr ( arg , ' = ' ) ) ) ;
if ( code & & code ! = 301 & & code ! = 302 & & code ! = 307 & & code ! = 308 ) {
CHECK_GT ( p - arg , 0 ) ;
fprintf ( stderr , " error: unsupported redirect code %d \n " , code ) ;
r . path = arg ;
exit ( 1 ) ;
r . pathlen = p - arg ;
}
r . location = p + 1 ;
r . code = code ;
r . path = strdup ( src ) ;
r . pathlen = strlen ( src ) ;
r . location = strdup ( dst ) ;
if ( ( i = FindRedirect ( r . path , r . pathlen ) ) ! = - 1 ) {
if ( ( i = FindRedirect ( r . path , r . pathlen ) ) ! = - 1 ) {
redirects . p [ i ] = r ;
redirects . p [ i ] = r ;
} else {
} else {
@ -463,6 +475,17 @@ static void AddRedirect(const char *arg) {
}
}
}
}
static void ProgramRedirectArg ( int code , const char * arg ) {
char * s ;
const char * p ;
if ( ! ( p = strchr ( arg , ' = ' ) ) ) {
fprintf ( stderr , " error: redirect arg missing '=' \n " ) ;
exit ( 1 ) ;
}
ProgramRedirect ( code , ( s = strndup ( arg , p - arg ) ) , p + 1 ) ;
free ( s ) ;
}
static int CompareInts ( const uint64_t x , uint64_t y ) {
static int CompareInts ( const uint64_t x , uint64_t y ) {
return x > y ? 1 : x < y ? - 1 : 0 ;
return x > y ? 1 : x < y ? - 1 : 0 ;
}
}
@ -512,15 +535,32 @@ static void DescribeAddress(char buf[32], const struct sockaddr_in *addr) {
* p = ' \0 ' ;
* p = ' \0 ' ;
}
}
static void ProgramBrand ( const char * s ) {
free ( brand ) ;
free ( serverheader ) ;
brand = strdup ( s ) ;
if ( ! strstr ( s , " redbean " ) ) unbranded = true ;
if ( ! ( serverheader = EncodeHttpHeaderValue ( brand , - 1 , 0 ) ) ) {
fprintf ( stderr , " error: brand isn't latin1 encodable: %`'s " , brand ) ;
exit ( 1 ) ;
}
}
static void ProgramCache ( long x ) {
cacheseconds = x ;
}
static void ProgramPort ( long x ) {
serveraddr . sin_port = htons ( x ) ;
}
static void SetDefaults ( void ) {
static void SetDefaults ( void ) {
cacheseconds = - 1 ;
ProgramBrand ( " redbean/0.2 " ) ;
serverheader = " redbean/0.2 " ;
ProgramCache ( - 1 ) ;
ProgramPort ( DEFAULT_PORT ) ;
serveraddr . sin_family = AF_INET ;
serveraddr . sin_family = AF_INET ;
serveraddr . sin_port = htons ( DEFAULT_PORT ) ;
serveraddr . sin_addr . s_addr = INADDR_ANY ;
serveraddr . sin_addr . s_addr = INADDR_ANY ;
AddRedirect ( " /=/tool/net/redbean.html " ) ;
if ( IsWindows ( ) ) uniprocess = true ;
AddRedirect ( " /index.html=/tool/net/redbean.html " ) ;
AddRedirect ( " /favicon.ico=/tool/net/redbean.ico " ) ;
}
}
static wontreturn void PrintUsage ( FILE * f , int rc ) {
static wontreturn void PrintUsage ( FILE * f , int rc ) {
@ -548,7 +588,7 @@ static void AddStagingDirectory(const char *dirpath) {
static void GetOpts ( int argc , char * argv [ ] ) {
static void GetOpts ( int argc , char * argv [ ] ) {
int opt ;
int opt ;
while ( ( opt = getopt ( argc , argv , " zhduvmbl:p:w:r:c:L:P:U:G:B:D: " ) ) ! = - 1 ) {
while ( ( opt = getopt ( argc , argv , " zhduvmbl:p:w:r:R: c:L:P:U:G:B:D: " ) ) ! = - 1 ) {
switch ( opt ) {
switch ( opt ) {
case ' v ' :
case ' v ' :
__log_level + + ;
__log_level + + ;
@ -568,23 +608,29 @@ static void GetOpts(int argc, char *argv[]) {
case ' z ' :
case ' z ' :
printport = true ;
printport = true ;
break ;
break ;
case ' k ' :
encouragekeepalive = true ;
break ;
case ' r ' :
case ' r ' :
Add Redirect( optarg ) ;
Program RedirectArg ( 307 , optarg ) ;
break ;
case ' R ' :
ProgramRedirectArg ( 0 , optarg ) ;
break ;
break ;
case ' D ' :
case ' D ' :
AddStagingDirectory ( optarg ) ;
AddStagingDirectory ( optarg ) ;
break ;
break ;
case ' c ' :
case ' c ' :
cacheseconds = strtol ( optarg , NULL , 0 ) ;
ProgramCache ( strtol ( optarg , NULL , 0 ) ) ;
break ;
break ;
case ' p ' :
case ' p ' :
CHECK_NE ( 0xFFFF , ( serveraddr . sin_port = htons ( parseport ( optarg ) ) ) ) ;
ProgramPort ( strtol ( optarg , NULL , 0 ) ) ;
break ;
break ;
case ' l ' :
case ' l ' :
CHECK_EQ ( 1 , inet_pton ( AF_INET , optarg , & serveraddr . sin_addr ) ) ;
CHECK_EQ ( 1 , inet_pton ( AF_INET , optarg , & serveraddr . sin_addr ) ) ;
break ;
break ;
case ' B ' :
case ' B ' :
serverheader = emptytonull ( EncodeHttpHeaderValue ( optarg , - 1 , 0 ) ) ;
ProgramBrand ( optarg ) ;
break ;
break ;
case ' L ' :
case ' L ' :
logpath = optarg ;
logpath = optarg ;
@ -631,14 +677,18 @@ static void Daemonize(void) {
}
}
static void OnWorkerExit ( int pid , int ws ) {
static void OnWorkerExit ( int pid , int ws ) {
int w ;
w = - - shared - > workers ;
if ( WIFEXITED ( ws ) ) {
if ( WIFEXITED ( ws ) ) {
if ( WEXITSTATUS ( ws ) ) {
if ( WEXITSTATUS ( ws ) ) {
WARNF ( " worker %d exited with %d " , pid , WEXITSTATUS ( ws ) ) ;
WARNF ( " worker %d exited with %d (%,d workers remain) " , pid ,
WEXITSTATUS ( ws ) , w ) ;
} else {
} else {
DEBUGF ( " worker %d exited " , pid ) ;
DEBUGF ( " worker %d exited (%,d workers remain) " , pid , w );
}
}
} else {
} else {
WARNF ( " worker %d terminated with %s " , pid , strsignal ( WTERMSIG ( ws ) ) ) ;
WARNF ( " worker %d terminated with %s (%,d workers remain) " , pid ,
strsignal ( WTERMSIG ( ws ) ) , w ) ;
}
}
}
}
@ -651,7 +701,9 @@ static void WaitAll(void) {
if ( errno = = ECHILD ) break ;
if ( errno = = ECHILD ) break ;
if ( errno = = EINTR ) {
if ( errno = = EINTR ) {
if ( killed ) {
if ( killed ) {
WARNF ( " %s terminating harder " , serveraddrstr ) ;
killed = false ;
terminated = false ;
WARNF ( " %s redbean shall terminate harder " , serveraddrstr ) ;
LOGIFNEG1 ( kill ( 0 , SIGTERM ) ) ;
LOGIFNEG1 ( kill ( 0 , SIGTERM ) ) ;
}
}
continue ;
continue ;
@ -803,11 +855,18 @@ static void *FreeLater(void *p) {
static void CollectGarbage ( void ) {
static void CollectGarbage ( void ) {
size_t i ;
size_t i ;
for ( i = 0 ; i < freelist . n ; + + i ) free ( freelist . p [ i ] ) ;
for ( i = 0 ; i < freelist . n ; + + i ) free ( freelist . p [ i ] ) ;
free ( freelist . p ) ;
freelist . p = 0 ;
freelist . n = 0 ;
freelist . n = 0 ;
free ( outbuf . p ) ;
free ( request . params . p ) ;
free ( request . params . p ) ;
DestroyHttpRequest ( & msg ) ;
DestroyHttpRequest ( & msg ) ;
}
}
static bool IsCompressionMethodSupported ( int method ) {
return method = = kZipCompressionNone | | method = = kZipCompressionDeflate ;
}
static void IndexAssets ( void ) {
static void IndexAssets ( void ) {
int64_t lm ;
int64_t lm ;
struct Asset * p ;
struct Asset * p ;
@ -819,6 +878,12 @@ static void IndexAssets(void) {
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 ) ) ;
if ( ! IsCompressionMethodSupported ( ZIP_CFILE_COMPRESSIONMETHOD ( zmap + cf ) ) ) {
WARNF ( " don't understand zip compression method %d used by %`'.*s " ,
ZIP_CFILE_COMPRESSIONMETHOD ( zmap + cf ) ,
ZIP_CFILE_NAMESIZE ( zmap + cf ) , ZIP_CFILE_NAME ( 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 {
@ -1019,7 +1084,7 @@ static void ParseFragment(struct Parser *u, struct Buffer *h) {
u - > q = u - > p ;
u - > q = u - > p ;
}
}
static bool ParseRequestUri ( void ) {
static void ParseRequestUri ( void ) {
struct Parser u ;
struct Parser u ;
u . i = 0 ;
u . i = 0 ;
u . c = 0 ;
u . c = 0 ;
@ -1032,8 +1097,6 @@ static bool ParseRequestUri(void) {
ParsePath ( & u , & request . path ) ;
ParsePath ( & u , & request . path ) ;
if ( u . c = = ' ? ' ) ParseParams ( & u , & request . params ) ;
if ( u . c = = ' ? ' ) ParseParams ( & u , & request . params ) ;
if ( u . c = = ' # ' ) ParseFragment ( & u , & request . fragment ) ;
if ( u . c = = ' # ' ) ParseFragment ( & u , & request . fragment ) ;
return u . i = = u . size & &
IsAcceptableHttpRequestPath ( request . path . p , request . path . n ) ;
}
}
static void ParseFormParams ( void ) {
static void ParseFormParams ( void ) {
@ -1310,6 +1373,43 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
return p ;
return p ;
}
}
static void AppendData ( const char * data , size_t size ) {
outbuf . p = xrealloc ( outbuf . p , outbuf . n + size ) ;
memcpy ( outbuf . p + outbuf . n , data , size ) ;
outbuf . n + = size ;
}
static void AppendString ( const char * s ) {
AppendData ( s , strlen ( s ) ) ;
}
static void AppendFmt ( const char * fmt , . . . ) {
int size ;
char * data ;
va_list va ;
data = NULL ;
va_start ( va , fmt ) ;
CHECK_NE ( - 1 , ( size = vasprintf ( & data , fmt , va ) ) ) ;
va_end ( va ) ;
AppendData ( data , size ) ;
free ( data ) ;
}
static char * CommitOutput ( char * p ) {
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 ;
}
return p ;
}
static int LuaServeAsset ( lua_State * L ) {
static int LuaServeAsset ( lua_State * L ) {
size_t pathlen ;
size_t pathlen ;
struct Asset * a ;
struct Asset * a ;
@ -1389,11 +1489,6 @@ static int LuaGetPath(lua_State *L) {
return 1 ;
return 1 ;
}
}
static int LuaGetFragment ( lua_State * L ) {
lua_pushlstring ( L , request . fragment . p , request . fragment . n ) ;
return 1 ;
}
static void LuaPushLatin1 ( lua_State * L , const char * s , size_t n ) {
static void LuaPushLatin1 ( lua_State * L , const char * s , size_t n ) {
char * t ;
char * t ;
size_t m ;
size_t m ;
@ -1588,9 +1683,7 @@ static int LuaWrite(lua_State *L) {
size_t size ;
size_t size ;
const char * data ;
const char * data ;
data = luaL_checklstring ( L , 1 , & size ) ;
data = luaL_checklstring ( L , 1 , & size ) ;
outbuf . p = xrealloc ( outbuf . p , outbuf . n + size ) ;
AppendData ( data , size ) ;
memcpy ( outbuf . p + outbuf . n , data , size ) ;
outbuf . n + = size ;
return 0 ;
return 0 ;
}
}
@ -1697,11 +1790,58 @@ static int LuaCrc32c(lua_State *L) {
return 1 ;
return 1 ;
}
}
static int LuaProgramPort ( lua_State * L ) {
ProgramPort ( luaL_checkinteger ( L , 1 ) ) ;
return 0 ;
}
static int LuaProgramCache ( lua_State * L ) {
ProgramCache ( luaL_checkinteger ( L , 1 ) ) ;
return 0 ;
}
static int LuaProgramBrand ( lua_State * L ) {
ProgramBrand ( luaL_checkstring ( L , 1 ) ) ;
return 0 ;
}
static int LuaProgramRedirect ( lua_State * L ) {
ProgramRedirect ( luaL_checkinteger ( L , 1 ) , luaL_checkstring ( L , 2 ) ,
luaL_checkstring ( L , 3 ) ) ;
return 0 ;
}
static int LuaGetLogLevel ( lua_State * L ) {
lua_pushinteger ( L , __log_level ) ;
return 1 ;
}
static int LuaSetLogLevel ( lua_State * L ) {
__log_level = luaL_checkinteger ( L , 1 ) ;
return 0 ;
}
static int LuaLog ( lua_State * L ) {
int level ;
lua_Debug ar ;
const char * msg , * module ;
level = luaL_checkinteger ( L , 1 ) ;
if ( LOGGABLE ( level ) ) {
msg = luaL_checkstring ( L , 2 ) ;
lua_getstack ( L , 1 , & ar ) ;
lua_getinfo ( L , " nSl " , & ar ) ;
module = ! strcmp ( ar . name , " main " ) ? sauce : ar . name ;
flogf ( level , module , ar . currentline , NULL , " %s " , msg ) ;
}
return 0 ;
}
static void LuaRun ( const char * path ) {
static void LuaRun ( const char * path ) {
struct Asset * a ;
struct Asset * a ;
const char * code ;
const char * code ;
if ( ( a = LocateAsset ( path , strlen ( path ) ) ) ) {
if ( ( a = LocateAsset ( path , strlen ( path ) ) ) ) {
code = LoadAsset ( a , NULL ) ;
code = LoadAsset ( a , NULL ) ;
sauce = path + 1 ;
if ( luaL_dostring ( L , code ) ! = LUA_OK ) {
if ( luaL_dostring ( L , code ) ! = LUA_OK ) {
WARNF ( " %s %s " , path , lua_tostring ( L , - 1 ) ) ;
WARNF ( " %s %s " , path , lua_tostring ( L , - 1 ) ) ;
}
}
@ -1723,9 +1863,9 @@ static const luaL_Reg kLuaFuncs[] = {
{ " FormatHttpDateTime " , LuaFormatHttpDateTime } , //
{ " FormatHttpDateTime " , LuaFormatHttpDateTime } , //
{ " GetClientAddr " , LuaGetClientAddr } , //
{ " GetClientAddr " , LuaGetClientAddr } , //
{ " GetDate " , LuaGetDate } , //
{ " GetDate " , LuaGetDate } , //
{ " GetFragment " , LuaGetFragment } , //
{ " GetHeader " , LuaGetHeader } , //
{ " GetHeader " , LuaGetHeader } , //
{ " GetHeaders " , LuaGetHeaders } , //
{ " GetHeaders " , LuaGetHeaders } , //
{ " GetLogLevel " , LuaGetLogLevel } , //
{ " GetMethod " , LuaGetMethod } , //
{ " GetMethod " , LuaGetMethod } , //
{ " GetParam " , LuaGetParam } , //
{ " GetParam " , LuaGetParam } , //
{ " GetParams " , LuaGetParams } , //
{ " GetParams " , LuaGetParams } , //
@ -1736,10 +1876,16 @@ static const luaL_Reg kLuaFuncs[] = {
{ " GetVersion " , LuaGetVersion } , //
{ " GetVersion " , LuaGetVersion } , //
{ " HasParam " , LuaHasParam } , //
{ " HasParam " , LuaHasParam } , //
{ " LoadAsset " , LuaLoadAsset } , //
{ " LoadAsset " , LuaLoadAsset } , //
{ " Log " , LuaLog } , //
{ " ParseHttpDateTime " , LuaParseHttpDateTime } , //
{ " ParseHttpDateTime " , LuaParseHttpDateTime } , //
{ " ProgramBrand " , LuaProgramBrand } , //
{ " ProgramCache " , LuaProgramCache } , //
{ " ProgramPort " , LuaProgramPort } , //
{ " ProgramRedirect " , LuaProgramRedirect } , //
{ " ServeAsset " , LuaServeAsset } , //
{ " ServeAsset " , LuaServeAsset } , //
{ " ServeError " , LuaServeError } , //
{ " ServeError " , LuaServeError } , //
{ " SetHeader " , LuaSetHeader } , //
{ " SetHeader " , LuaSetHeader } , //
{ " SetLogLevel " , LuaSetLogLevel } , //
{ " SetStatus " , LuaSetStatus } , //
{ " SetStatus " , LuaSetStatus } , //
{ " Write " , LuaWrite } , //
{ " Write " , LuaWrite } , //
{ " bsf " , LuaBsf } , //
{ " bsf " , LuaBsf } , //
@ -1749,7 +1895,7 @@ static const luaL_Reg kLuaFuncs[] = {
{ " popcnt " , LuaPopcnt } , //
{ " popcnt " , LuaPopcnt } , //
} ;
} ;
static void LuaSetArgv ( void ) {
static void LuaSetArgv ( lua_State * L ) {
size_t i ;
size_t i ;
lua_newtable ( L ) ;
lua_newtable ( L ) ;
for ( i = optind ; i < __argc ; + + i ) {
for ( i = optind ; i < __argc ; + + i ) {
@ -1759,6 +1905,11 @@ static void LuaSetArgv(void) {
lua_setglobal ( L , " argv " ) ;
lua_setglobal ( L , " argv " ) ;
}
}
static void LuaSetConstant ( lua_State * L , const char * s , long x ) {
lua_pushinteger ( L , x ) ;
lua_setglobal ( L , s ) ;
}
static void LuaInit ( void ) {
static void LuaInit ( void ) {
size_t i ;
size_t i ;
L = luaL_newstate ( ) ;
L = luaL_newstate ( ) ;
@ -1767,7 +1918,13 @@ static void LuaInit(void) {
lua_pushcfunction ( L , kLuaFuncs [ i ] . func ) ;
lua_pushcfunction ( L , kLuaFuncs [ i ] . func ) ;
lua_setglobal ( L , kLuaFuncs [ i ] . name ) ;
lua_setglobal ( L , kLuaFuncs [ i ] . name ) ;
}
}
LuaSetArgv ( ) ;
LuaSetArgv ( L ) ;
LuaSetConstant ( L , " kLogDebug " , kLogDebug ) ;
LuaSetConstant ( L , " kLogVerbose " , kLogVerbose ) ;
LuaSetConstant ( L , " kLogInfo " , kLogInfo ) ;
LuaSetConstant ( L , " kLogWarn " , kLogWarn ) ;
LuaSetConstant ( L , " kLogError " , kLogError ) ;
LuaSetConstant ( L , " kLogFatal " , kLogFatal ) ;
LuaRun ( " /tool/net/.init.lua " ) ;
LuaRun ( " /tool/net/.init.lua " ) ;
}
}
@ -1777,25 +1934,15 @@ static void LuaReload(void) {
static char * ServeLua ( struct Asset * a ) {
static char * ServeLua ( struct Asset * a ) {
char * p ;
char * p ;
outbuf . n = 0 ;
luaheaderp = NULL ;
luaheaderp = NULL ;
sauce = FreeLater ( strndup ( request . path . p + 1 , request . path . n - 1 ) ) ;
if ( luaL_dostring ( L , FreeLater ( LoadAsset ( a , NULL ) ) ) = = LUA_OK ) {
if ( luaL_dostring ( L , FreeLater ( LoadAsset ( a , NULL ) ) ) = = LUA_OK ) {
if ( ! ( p = luaheaderp ) ) {
if ( ! ( p = luaheaderp ) ) {
p = SetStatus ( 200 , " OK " ) ;
p = SetStatus ( 200 , " OK " ) ;
p = AppendContentType ( p , " text/html " ) ;
p = AppendContentType ( p , " text/html " ) ;
}
}
if ( outbuf . n ) {
if ( outbuf . n ) {
if ( istext & & outbuf . n > = 100 & & ClientAcceptsGzip ( ) ) {
p = CommitOutput ( p ) ;
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 ;
}
}
}
return p ;
return p ;
} else {
} else {
@ -1828,24 +1975,25 @@ static char *HandleAsset(struct Asset *a, const char *path, size_t pathlen) {
}
}
static char * HandleRedirect ( struct Redirect * r ) {
static char * HandleRedirect ( struct Redirect * r ) {
char * p ;
struct Asset * a ;
struct Asset * a ;
if ( ! r - > code ) {
if ( ( a = LocateAsset ( r - > location , strlen ( r - > location ) ) ) ) {
if ( ( a = LocateAsset ( r - > location , strlen ( r - > location ) ) ) ) {
DEBUGF ( " %s %s %`'.*s rewritten %`'s " , clientaddrstr ,
DEBUGF ( " %s %s %`'.*s rewritten %`'s " , clientaddrstr ,
kHttpMethod [ msg . method ] , request . path . n , request . path . p ,
kHttpMethod [ msg . method ] , request . path . n , request . path . p ,
r - > location ) ;
r - > location ) ;
p = HandleAsset ( a , r - > location , strlen ( r - > location ) ) ;
return HandleAsset ( a , r - > location , strlen ( r - > location ) ) ;
} else if ( httpversion = = 9 ) {
p = ServeError ( 505 , " HTTP Version Not Supported " ) ;
} else {
} else {
DEBUGF ( " %s %s %`'.*s redirecting %`'s " , clientaddrstr ,
return ServeError ( 505 , " HTTP Version Not Supported " ) ;
kHttpMethod [ msg . method ] , request . path . n , request . path . p ,
}
} else if ( httpversion = = 9 ) {
return ServeError ( 505 , " HTTP Version Not Supported " ) ;
} else {
DEBUGF ( " %s %s %`'.*s %d redirecting %`'s " , clientaddrstr ,
kHttpMethod [ msg . method ] , request . path . n , request . path . p , r - > code ,
r - > location ) ;
r - > location ) ;
p = SetStatus ( 307 , " Temporary Redirect " ) ;
return AppendHeader ( SetStatus ( r - > code , GetHttpReason ( r - > code ) ) , " Location " ,
p = AppendHeader ( p , " Location " ,
FreeLater ( EncodeHttpHeaderValue ( r - > location , - 1 , 0 ) ) ) ;
FreeLater ( EncodeHttpHeaderValue ( r - > location , - 1 , 0 ) ) ) ;
}
}
return p ;
}
}
static void LogMessage ( const char * d , const char * s , size_t n ) {
static void LogMessage ( const char * d , const char * s , size_t n ) {
@ -1930,6 +2078,140 @@ static const char *DescribeClose(void) {
return " destroyed " ;
return " destroyed " ;
}
}
static const char * DescribeCompressionRatio ( char rb [ 8 ] , uint32_t cf ) {
long percent ;
if ( ZIP_CFILE_COMPRESSIONMETHOD ( zmap + cf ) = = kZipCompressionNone ) {
return " n/a " ;
} else {
percent = lround ( 100 - ( double ) ZIP_CFILE_COMPRESSEDSIZE ( zmap + cf ) /
ZIP_CFILE_UNCOMPRESSEDSIZE ( zmap + cf ) * 100 ) ;
sprintf ( rb , " %ld%% " , MIN ( 999 , MAX ( - 999 , percent ) ) ) ;
return rb ;
}
}
static void LoadLogo ( void ) {
char * p ;
size_t n ;
struct Asset * a ;
const char * logopath ;
logopath = " /tool/net/redbean.png " ;
if ( ( a = LocateAsset ( logopath , strlen ( logopath ) ) ) ) {
p = LoadAsset ( a , & n ) ;
logo . p = EncodeBase64 ( p , n , & logo . n ) ;
free ( p ) ;
}
}
static char * ServeListing ( void ) {
char rb [ 8 ] ;
char tb [ 64 ] ;
int w , x , y ;
struct tm tm ;
int64_t lastmod ;
uint32_t cf , lf ;
size_t n , n1 , n2 ;
char * p , * p1 , * p2 ;
struct EscapeResult r [ 3 ] ;
AppendString ( " \
<!doctype html> \n \
<meta charset= \" utf-8 \" > \n \
<title>redbean zip listing</title> \n \
<style> \n \
html { \n \
color: #111; \n \
font-family: sans-serif; \n \
} \n \
a { \n \
text-decoration: none; \n \
} \n \
img { \n \
vertical-align: middle; \n \
} \n \
</style> \n \
<h1> \n " ) ;
if ( logo . n ) {
AppendString ( " <img src= \" data:image/png;base64, " ) ;
AppendData ( logo . p , logo . n ) ;
AppendString ( " \" > \n " ) ;
}
r [ 0 ] = EscapeHtml ( brand , strlen ( brand ) ) ;
AppendData ( r [ 0 ] . data , r [ 0 ] . size ) ;
free ( r [ 0 ] . data ) ;
AppendString ( " </h1><hr><pre> \n " ) ;
w = x = 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 ) ) ;
y = strnwidth ( ZIP_CFILE_NAME ( zmap + cf ) , ZIP_CFILE_NAMESIZE ( zmap + cf ) , 0 ) ;
w = MIN ( 80 , MAX ( w , y + 2 ) ) ;
y = ZIP_CFILE_UNCOMPRESSEDSIZE ( zmap + cf ) ;
y = y ? llog10 ( y ) : 1 ;
x = MIN ( 80 , MAX ( x , y + ( y - 1 ) / 3 + 2 ) ) ;
}
n = ZIP_CDIR_RECORDS ( zdir ) ;
for ( cf = ZIP_CDIR_OFFSET ( zdir ) ; n - - ; cf + = ZIP_CFILE_HDRSIZE ( zmap + cf ) ) {
CHECK_EQ ( kZipCfileHdrMagic , ZIP_CFILE_MAGIC ( zmap + cf ) ) ;
p1 = ZIP_CFILE_NAME ( zmap + cf ) ;
n1 = ZIP_CFILE_NAMESIZE ( zmap + cf ) ;
p2 = malloc ( 1 + n1 ) ;
n2 = 1 + n1 ;
* 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 ) ;
lastmod = GetLastModifiedZip ( zmap + cf ) ;
localtime_r ( & lastmod , & tm ) ;
strftime ( tb , sizeof ( tb ) , " %Y-%m-%d %H:%M:%S " , & tm ) ;
if ( IsCompressionMethodSupported ( ZIP_CFILE_COMPRESSIONMETHOD ( zmap + cf ) ) & &
IsAcceptableHttpRequestPath ( p2 , n2 ) ) {
AppendFmt ( " <a href= \" %.*s \" >%-*.*s</a> %s %4s %,*ld \n " , r [ 2 ] . size ,
r [ 2 ] . data , w , r [ 0 ] . size , r [ 0 ] . data , tb ,
DescribeCompressionRatio ( rb , cf ) , x ,
ZIP_CFILE_UNCOMPRESSEDSIZE ( zmap + cf ) ) ;
} else {
AppendFmt ( " %-*.*s %s %4s %,*ld \n " , w , r [ 0 ] . size , r [ 0 ] . data , tb ,
DescribeCompressionRatio ( rb , cf ) , x ,
ZIP_CFILE_UNCOMPRESSEDSIZE ( zmap + cf ) ) ;
}
free ( r [ 2 ] . data ) ;
free ( r [ 1 ] . data ) ;
free ( r [ 0 ] . data ) ;
free ( p2 ) ;
}
AppendString ( " </pre><hr><p> \n " ) ;
if ( ! unbranded ) {
AppendString ( " \
this listing is for / \n \
when there's no /index.lua or /index.html in your zip<br> \n \
<a href= \" https://justine.lol/redbean/index.html \" >redbean</a> is based on \n \
<a href= \" https://github.com/jart/cosmopolitan \" >cosmopolitan</a> and \n \
<a href= \" https://justine.storage.googleapis.com/ape.html \" >α cτµα lly \n \
pδrτα blε εxεcµτα blε</a><br> \n \
redbean is authored by Justine Tunney who's on \n \
<a href= \" https://github.com/jart \" >GitHub</a> and \n \
<a href= \" https://twitter.com/JustineTunney \" >Twitter</a><br> \n \
your redbean is " ) ;
}
w = shared - > workers ;
AppendFmt ( " currently servicing %,d connection%s \n " , w , w = = 1 ? " " : " s " ) ;
p = SetStatus ( 200 , " OK " ) ;
p = AppendCache ( p , 0 ) ;
return CommitOutput ( p ) ;
}
static char * ServeServerOptions ( void ) {
char * p ;
p = SetStatus ( 200 , " OK " ) ;
p = AppendHeader ( p , " Accept " , " */* " ) ;
p = AppendHeader ( p , " Accept-Charset " , " utf-8 " ) ;
p = AppendHeader ( p , " Allow " , " GET, HEAD, POST, PUT, DELETE, OPTIONS " ) ;
VERBOSEF ( " %s pinged our server with OPTIONS * " , clientaddrstr ) ;
return p ;
}
static char * HandleMessage ( void ) {
static char * HandleMessage ( void ) {
long r ;
long r ;
ssize_t cl , rc ;
ssize_t cl , rc ;
@ -1940,7 +2222,7 @@ static char *HandleMessage(void) {
if ( httpversion > 101 ) {
if ( httpversion > 101 ) {
return ServeError ( 505 , " HTTP Version Not Supported " ) ;
return ServeError ( 505 , " HTTP Version Not Supported " ) ;
}
}
if ( msg . method > kHttpPost | |
if ( msg . method > kHttpOptions | |
( HasHeader ( kHttpTransferEncoding ) & &
( HasHeader ( kHttpTransferEncoding ) & &
! HeaderEquals ( kHttpTransferEncoding , " identity " ) ) ) {
! HeaderEquals ( kHttpTransferEncoding , " identity " ) ) ) {
return ServeError ( 501 , " Not Implemented " ) ;
return ServeError ( 501 , " Not Implemented " ) ;
@ -1954,7 +2236,7 @@ static char *HandleMessage(void) {
if ( HasHeader ( kHttpContentLength ) ) {
if ( HasHeader ( kHttpContentLength ) ) {
return ServeError ( 400 , " Bad Request " ) ;
return ServeError ( 400 , " Bad Request " ) ;
} else if ( msg . method ! = kHttpGet & & msg . method ! = kHttpHead & &
} else if ( msg . method ! = kHttpGet & & msg . method ! = kHttpHead & &
msg . method ! = kHttpOptions ) {
msg . method ! = kHttpDelete & & msg . method ! = kHttpOptions ) {
return ServeError ( 411 , " Length Required " ) ;
return ServeError ( 411 , " Length Required " ) ;
} else {
} else {
cl = 0 ;
cl = 0 ;
@ -1969,7 +2251,7 @@ static char *HandleMessage(void) {
}
}
while ( amtread < need ) {
while ( amtread < need ) {
if ( + + frags = = 64 ) {
if ( + + frags = = 64 ) {
LogClose ( " payload fragged " ) ;
LogClose ( " payload fragged! " ) ;
return ServeError ( 408 , " Request Timeout " ) ;
return ServeError ( 408 , " Request Timeout " ) ;
}
}
if ( ( rc = read ( client , inbuf . p + amtread , inbuf . n - amtread ) ) ! = - 1 ) {
if ( ( rc = read ( client , inbuf . p + amtread , inbuf . n - amtread ) ) ! = - 1 ) {
@ -1996,7 +2278,12 @@ static char *HandleMessage(void) {
if ( httpversion ! = 101 | | IsConnectionClose ( ) ) {
if ( httpversion ! = 101 | | IsConnectionClose ( ) ) {
connectionclose = true ;
connectionclose = true ;
}
}
if ( ! ParseRequestUri ( ) ) {
ParseRequestUri ( ) ;
if ( msg . method = = kHttpOptions & &
! CompareSlices ( request . path . p , request . path . n , " * " , 1 ) ) {
return ServeServerOptions ( ) ;
}
if ( ! IsAcceptableHttpRequestPath ( request . path . p , request . path . n ) ) {
WARNF ( " %s could not parse request request %`'.*s " , clientaddrstr ,
WARNF ( " %s could not parse request request %`'.*s " , clientaddrstr ,
msg . uri . b - msg . uri . a , inbuf . p + msg . uri . a ) ;
msg . uri . b - msg . uri . a , inbuf . p + msg . uri . a ) ;
connectionclose = true ;
connectionclose = true ;
@ -2013,6 +2300,8 @@ static char *HandleMessage(void) {
return HandleAsset ( a , 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 ) {
} else if ( ( r = FindRedirect ( request . path . p , request . path . n ) ) ! = - 1 ) {
return HandleRedirect ( redirects . p + r ) ;
return HandleRedirect ( redirects . p + r ) ;
} else if ( ! CompareSlices ( request . path . p , request . path . n , " / " , 1 ) ) {
return ServeListing ( ) ;
} else {
} else {
return ServeError ( 404 , " Not Found " ) ;
return ServeError ( 404 , " Not Found " ) ;
}
}
@ -2051,6 +2340,8 @@ static bool HandleRequest(void) {
}
}
if ( connectionclose ) {
if ( connectionclose ) {
p = AppendHeader ( p , " Connection " , " close " ) ;
p = AppendHeader ( p , " Connection " , " close " ) ;
} else if ( encouragekeepalive & & httpversion > = 101 ) {
p = AppendHeader ( p , " Connection " , " keep-alive " ) ;
}
}
actualcontentlength = contentlength ;
actualcontentlength = contentlength ;
if ( gzipped ) {
if ( gzipped ) {
@ -2091,6 +2382,8 @@ static bool HandleRequest(void) {
static void InitRequest ( void ) {
static void InitRequest ( void ) {
frags = 0 ;
frags = 0 ;
msgsize = 0 ;
msgsize = 0 ;
outbuf . p = 0 ;
outbuf . n = 0 ;
content = NULL ;
content = NULL ;
gzipped = false ;
gzipped = false ;
branded = false ;
branded = false ;
@ -2118,7 +2411,7 @@ static void ProcessRequests(void) {
} else if ( got ) {
} else if ( got ) {
if ( + + frags = = 32 ) {
if ( + + frags = = 32 ) {
SendTimeout ( ) ;
SendTimeout ( ) ;
LogClose ( " fragged " ) ;
LogClose ( " fragged! " ) ;
return ;
return ;
} else {
} else {
DEBUGF ( " %s fragmented msg %,ld %,ld " , clientaddrstr , amtread ,
DEBUGF ( " %s fragmented msg %,ld %,ld " , clientaddrstr , amtread ,
@ -2151,6 +2444,13 @@ static void ProcessRequests(void) {
}
}
}
}
static void EnterMeltdownMode ( void ) {
if ( lastmeltdown & & nowl ( ) - lastmeltdown < 1 ) return ;
WARNF ( " redbean is entering meltdown mode with %,d workers " , shared - > workers ) ;
LOGIFNEG1 ( kill ( 0 , SIGUSR2 ) ) ;
lastmeltdown = nowl ( ) ;
}
static void ProcessConnection ( void ) {
static void ProcessConnection ( void ) {
int pid ;
int pid ;
clientaddrsize = sizeof ( clientaddr ) ;
clientaddrsize = sizeof ( clientaddr ) ;
@ -2167,11 +2467,11 @@ static void ProcessConnection(void) {
connectionclose = false ;
connectionclose = false ;
break ;
break ;
case - 1 :
case - 1 :
WARNF ( " redbean is entering m eltdown m ode" ) ;
EnterM eltdownM ode( ) ;
LOGIFNEG1 ( kill ( 0 , SIGUSR2 ) ) ;
SendServiceUnavailable ( ) ;
SendServiceUnavailable ( ) ;
/* fallthrough */
/* fallthrough */
default :
default :
+ + shared - > workers ;
close ( client ) ;
close ( client ) ;
return ;
return ;
}
}
@ -2195,12 +2495,20 @@ static void TuneServerSocket(void) {
LOGIFNEG1 ( setsockopt ( server , IPPROTO_TCP , TCP_QUICKACK , & yes , sizeof ( yes ) ) ) ;
LOGIFNEG1 ( setsockopt ( server , IPPROTO_TCP , TCP_QUICKACK , & yes , sizeof ( yes ) ) ) ;
}
}
void RedBean ( void ) {
void RedBean ( int argc , char * argv [ ] ) {
uint32_t addrsize ;
uint32_t addrsize ;
if ( IsWindows ( ) ) uniprocess = true ;
gmtoff = GetGmtOffset ( ) ;
gmtoff = GetGmtOffset ( ) ;
CHECK_NE ( MAP_FAILED ,
( shared = mmap ( NULL , ROUNDUP ( sizeof ( struct Shared ) , FRAMESIZE ) ,
PROT_READ | PROT_WRITE , MAP_SHARED | MAP_ANONYMOUS ,
- 1 , 0 ) ) ) ;
OpenZip ( ( const char * ) getauxval ( AT_EXECFN ) ) ;
OpenZip ( ( const char * ) getauxval ( AT_EXECFN ) ) ;
IndexAssets ( ) ;
IndexAssets ( ) ;
LoadLogo ( ) ;
SetDefaults ( ) ;
GetOpts ( argc , argv ) ;
LuaInit ( ) ;
if ( uniprocess ) shared - > workers = 1 ;
xsigaction ( SIGINT , OnInt , 0 , 0 , 0 ) ;
xsigaction ( SIGINT , OnInt , 0 , 0 , 0 ) ;
xsigaction ( SIGHUP , OnHup , 0 , 0 , 0 ) ;
xsigaction ( SIGHUP , OnHup , 0 , 0 , 0 ) ;
xsigaction ( SIGTERM , OnTerm , 0 , 0 , 0 ) ;
xsigaction ( SIGTERM , OnTerm , 0 , 0 , 0 ) ;
@ -2239,7 +2547,6 @@ void RedBean(void) {
inbuf . p = xvalloc ( inbuf . n ) ;
inbuf . p = xvalloc ( inbuf . n ) ;
hdrbuf . n = 4 * 1024 ;
hdrbuf . n = 4 * 1024 ;
hdrbuf . p = xvalloc ( hdrbuf . n ) ;
hdrbuf . p = xvalloc ( hdrbuf . n ) ;
LuaInit ( ) ;
while ( ! terminated ) {
while ( ! terminated ) {
if ( zombied ) {
if ( zombied ) {
ReapZombies ( ) ;
ReapZombies ( ) ;
@ -2256,7 +2563,7 @@ void RedBean(void) {
ProcessConnection ( ) ;
ProcessConnection ( ) ;
}
}
}
}
VERBOSEF ( " %s terminated " , serveraddrstr ) ;
VERBOSEF ( " %s shutting down " , serveraddrstr ) ;
LOGIFNEG1 ( close ( server ) ) ;
LOGIFNEG1 ( close ( server ) ) ;
if ( ! keyboardinterrupt ) {
if ( ! keyboardinterrupt ) {
if ( ! killed ) {
if ( ! killed ) {
@ -2269,8 +2576,6 @@ void RedBean(void) {
int main ( int argc , char * argv [ ] ) {
int main ( int argc , char * argv [ ] ) {
showcrashreports ( ) ;
showcrashreports ( ) ;
SetDefaults ( ) ;
RedBean ( argc , argv ) ;
GetOpts ( argc , argv ) ;
RedBean ( ) ;
return 0 ;
return 0 ;
}
}