mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 23:13:34 +00:00
redbean improvements: - Explicitly disable corking - Simulate Python regex API for Lua - Send warmup requests in main process on startup - Add Class-A granular IPv4 network classification - Add /statusz page so you can monitor your redbean's health - Fix regressions on OpenBSD/NetBSD caused by recent changes - Plug Authorization header into Lua GetUser and GetPass APIs - Recognize X-Forwarded-{For,Host} from local reverse proxies - Add many additional functions to redbean Lua server page API - Report resource usage of child processes on `/` listing page - Introduce `-a` flag for logging child process resource usage - Introduce `-t MILLIS` flag and `ProgramTimeout(ms)` init API - Introduce `-H "Header: value"` flag and `ProgramHeader(k,v)` API Cosmopolitan Libc improvements: - Make strerror() simpler - Make inet_pton() not depend on sscanf() - Fix OpenExecutable() which broke .data section earlier - Fix stdio in cases where it overflows kernel tty buffer - Fix bugs in crash reporting w/o .com.dbg binary present - Add polyfills for SO_LINGER, SO_RCVTIMEO, and SO_SNDTIMEO - Polyfill TCP_CORK on BSD and XNU using TCP_NOPUSH magnums New netcat clone in examples/nc.c: While testing some of the failure conditions for redbean, I noticed that BusyBox's `nc` command is pretty busted, if you use it as an interactive tool, rather than having it be part of a pipeline. Unfortunately this'll only work on UNIX since Windows doesn't let us poll on stdio and sockets at the same time because I don't think they want tools like this running on their platform. So if you want forbidden fruit, it's here so enjoy it
344 lines
11 KiB
Lua
344 lines
11 KiB
Lua
-- redbean lua server page demo
|
|
|
|
local function main()
|
|
-- This is the best way to print data to the console or log file.
|
|
Log(kLogWarn, "hello from \e[1mlua\e[0m!")
|
|
|
|
-- This check is pedantic but might be good to have.
|
|
if GetMethod() ~= 'GET' and GetMethod() ~= 'HEAD' then
|
|
ServeError(405)
|
|
SetHeader('Allow', 'GET, HEAD')
|
|
return
|
|
end
|
|
|
|
-- These two lines are optional.
|
|
-- The default behavior is to do this if you don't.
|
|
SetStatus(200) -- Shorthand for SetStatus(200, "OK")
|
|
SetHeader('Content-Type', 'text/html; charset=utf-8')
|
|
|
|
-- Response data is buffered until the script finishes running.
|
|
-- Compression is applied automatically, based on your headers.
|
|
Write('<!doctype html>\r\n')
|
|
Write('<title>redbean</title>\r\n')
|
|
Write('<h1>\r\n')
|
|
Write('<img style="vertical-align:middle" src="data:image/png;base64,\r\n')
|
|
Write(EncodeBase64(LoadAsset('/redbean.png')))
|
|
Write('">\r\n')
|
|
Write('redbean lua server page demo\r\n')
|
|
Write('</h1>\r\n')
|
|
|
|
-- Prevent caching.
|
|
-- We need this because we're doing things like putting the client's
|
|
-- IP address in the response so we naturally don't want that cached
|
|
SetHeader('Expires', FormatHttpDateTime(GetDate()))
|
|
SetHeader('Cache-Control', 'no-cache, must-revalidate, max-age=0')
|
|
|
|
-- Roundtripping information can make it safer.
|
|
Write('<p>Thank you for visiting ')
|
|
Write(EscapeHtml(EncodeUrl(ParseUrl(GetUrl()))))
|
|
Write('\r\n')
|
|
|
|
-- GetParam(NAME) is the fastest easiest way to get URL and FORM params
|
|
-- If you want the RequestURL query params specifically in full do this
|
|
Write('<h3>request url parameters</h3>\r\n')
|
|
params = ParseUrl(GetUrl()).params -- like GetParams() but w/o form body
|
|
if params and #params>0 then
|
|
Write('<dl>\r\n')
|
|
for i = 1,#params do
|
|
Write('<dt>')
|
|
Write(EscapeHtml(VisualizeControlCodes(params[i][1])))
|
|
Write('\r\n')
|
|
if params[i][2] then
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(params[i][2])))
|
|
Write('\r\n')
|
|
end
|
|
end
|
|
Write('</dl>\r\n')
|
|
else
|
|
Write('<p>\r\n')
|
|
Write('<em>none</em><br>\r\n')
|
|
Write('ProTip: Try <a href="')
|
|
Write(EscapeHtml(EscapePath(GetPath()) .. '?x=hi%20there%00%C0%80&y&z&z=' .. EscapeParam('&')))
|
|
Write('">clicking here</a>!\r\n')
|
|
end
|
|
|
|
-- redbean command line arguments
|
|
-- these come *after* the c getopt server arguments
|
|
Write('<h3>command line arguments</h3>\r\n')
|
|
if #argv > 0 then
|
|
Write('<ul>\r\n')
|
|
for i = 1,#argv do
|
|
Write('<li>')
|
|
Write(EscapeHtml(VisualizeControlCodes(argv[i])))
|
|
Write('\r\n')
|
|
end
|
|
Write('</ul>\r\n')
|
|
else
|
|
Write('<p><em>none</em>\r\n')
|
|
end
|
|
|
|
Write([[
|
|
<h3>post request html form demo</h3>
|
|
<form action="redbean-form.lua" method="post">
|
|
<input type="text" id="firstname" name="firstname">
|
|
<label for="firstname">first name</label>
|
|
<br>
|
|
<input type="text" id="lastname" name="lastname">
|
|
<label for="lastname">last name</label>
|
|
<br>
|
|
<input type="submit" value="Submit">
|
|
</form>
|
|
]])
|
|
|
|
Write([[
|
|
<h3>xmlhttprequest request demo</h3>
|
|
<input id="x" value="lâtìn1">
|
|
<label for="x">X-Custom-Header</label><br>
|
|
<button id="send">send</button><br>
|
|
<div id="result"></div>
|
|
<script>
|
|
function OnSend() {
|
|
var r = new XMLHttpRequest();
|
|
r.onload = function() {
|
|
document.getElementById("result").innerText = this.getResponseHeader('X-Custom-Header');
|
|
};
|
|
r.open('POST', 'redbean-xhr.lua');
|
|
r.setRequestHeader('X-Custom-Header', document.getElementById('x').value);
|
|
r.send();
|
|
}
|
|
document.getElementById('send').onclick = OnSend;
|
|
</script>
|
|
]])
|
|
|
|
-- fast redbean apis for accessing already parsed request data
|
|
Write('<h3>extra information</h3>\r\n')
|
|
Write('<dl>\r\n')
|
|
Write('<dt>GetMethod()\r\n')
|
|
Write('<dd>')
|
|
Write(EscapeHtml(GetMethod())) -- & and ' are legal in http methods
|
|
Write('\r\n')
|
|
if GetUser() then
|
|
Write('<dt>GetUser()\r\n')
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(GetUser())))
|
|
Write('\r\n')
|
|
end
|
|
if GetScheme() then
|
|
Write('<dt>GetScheme()\r\n')
|
|
Write('<dd>')
|
|
Write(GetScheme())
|
|
Write('\r\n')
|
|
end
|
|
if GetPass() then
|
|
Write('<dt>GetPass()\r\n')
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(GetPass())))
|
|
Write('\r\n')
|
|
end
|
|
Write('<dt>GetHost() <small>(from HTTP Request-URI or Host header or X-Forwarded-Host header or Berkeley Sockets)</small>\r\n')
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(GetHost())))
|
|
Write('\r\n')
|
|
Write('<dt>GetPort() <small>(from HTTP Request-URI or Host header or X-Forwarded-Host header or Berkeley Sockets)</small>\r\n')
|
|
Write('<dd>')
|
|
Write(tostring(GetPort()))
|
|
Write('\r\n')
|
|
Write('<dt>GetPath() <small>(from HTTP Request-URI)</small>\r\n')
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(GetPath())))
|
|
Write('\r\n')
|
|
Write('<dt>GetEffectivePath() <small>(actual path used internally to load the lua asset: routed depending on host, request path, and rewrites)</small>\r\n')
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(GetEffectivePath())))
|
|
Write('\r\n')
|
|
if GetFragment() then
|
|
Write('<dt>GetFragment()\r\n')
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(GetFragment())))
|
|
Write('\r\n')
|
|
end
|
|
Write('<dt>GetRemoteAddr() <small>(from Bekeley Sockets or X-Forwarded-For header)</small>\r\n')
|
|
Write('<dd>')
|
|
ip, port = GetRemoteAddr()
|
|
Write(string.format('%s, %d', FormatIp(ip), port))
|
|
if CategorizeIp(ip) then
|
|
Write('<br>\r\n')
|
|
Write(CategorizeIp(ip))
|
|
end
|
|
Write('\r\n')
|
|
Write('<dt>GetClientAddr()\r\n')
|
|
Write('<dd>')
|
|
ip, port = GetClientAddr()
|
|
Write(string.format('%s, %d', FormatIp(ip), port))
|
|
if CategorizeIp(ip) then
|
|
Write('<br>\r\n')
|
|
Write(CategorizeIp(ip))
|
|
end
|
|
Write('\r\n')
|
|
Write('<dt>GetServerIp()\r\n')
|
|
Write('<dd>')
|
|
ip, port = GetServerAddr()
|
|
Write(string.format('%s, %d', FormatIp(ip), port))
|
|
if CategorizeIp(ip) then
|
|
Write('<br>\r\n')
|
|
Write(CategorizeIp(ip))
|
|
end
|
|
Write('\r\n')
|
|
Write('</dl>\r\n')
|
|
|
|
-- redbean apis for generalized parsing and encoding
|
|
referer = GetHeader('Referer')
|
|
if referer then
|
|
url = ParseUrl(referer)
|
|
if url.scheme then
|
|
url.scheme = string.upper(url.scheme)
|
|
end
|
|
Write('<h3>referer url</h3>\r\n')
|
|
Write('<p>\r\n')
|
|
Write(EscapeHtml(EncodeUrl(url)))
|
|
Write('<dl>\r\n')
|
|
if url.scheme then
|
|
Write('<dt>scheme\r\n')
|
|
Write('<dd>\r\n')
|
|
Write(url.scheme)
|
|
end
|
|
if url.user then
|
|
Write('<dt>user\r\n')
|
|
Write('<dd>\r\n')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.user)))
|
|
end
|
|
if url.pass then
|
|
Write('<dt>pass\r\n')
|
|
Write('<dd>\r\n')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.pass)))
|
|
end
|
|
if url.host then
|
|
Write('<dt>host\r\n')
|
|
Write('<dd>\r\n')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.host)))
|
|
end
|
|
if url.port then
|
|
Write('<dt>port\r\n')
|
|
Write('<dd>\r\n')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.port)))
|
|
end
|
|
if url.path then
|
|
Write('<dt>path\r\n')
|
|
Write('<dd>\r\n')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.path)))
|
|
end
|
|
if url.params then
|
|
Write('<dt>params\r\n')
|
|
Write('<dd>\r\n')
|
|
Write('<dl>\r\n')
|
|
for i = 1,#url.params do
|
|
Write('<dt>')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.params[i][1])))
|
|
Write('\r\n')
|
|
if url.params[i][2] then
|
|
Write('<dd>')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.params[i][2])))
|
|
Write('\r\n')
|
|
end
|
|
end
|
|
Write('</dl>\r\n')
|
|
end
|
|
if url.fragment then
|
|
Write('<dt>fragment\r\n')
|
|
Write('<dd>\r\n')
|
|
Write(EscapeHtml(VisualizeControlCodes(url.fragment)))
|
|
end
|
|
Write('</dl>\r\n')
|
|
end
|
|
|
|
Write('<h3>posix extended regular expressions</h3>\r\n')
|
|
s = 'my ' .. FormatIp(GetRemoteAddr()) .. ' ip'
|
|
-- traditional regular expressions
|
|
m,a,b,c,d = re.search([[\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)]], s, re.BASIC)
|
|
-- easy api (~10x slower because compile is O(2ⁿ) and search is O(n))
|
|
m,a,b,c,d = re.search([[([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})]], s)
|
|
-- proper api (especially if you put the re.compile() line in /.init.lua)
|
|
pat = re.compile([[([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})]])
|
|
m,a,b,c,d = pat:search(s) -- m and rest are nil if match not found
|
|
Write('<pre>\r\n')
|
|
Write([[pat = re.compile('([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})', re.EXTENDED)]])
|
|
Write(string.format('\r\nm,a,b,c,d = pat:search(%q)\r\n', s))
|
|
Write('</pre>\r\n')
|
|
Write('<dl>\r\n')
|
|
Write('<dt>m\r\n')
|
|
Write('<dd>')
|
|
Write(tostring(m))
|
|
Write('\r\n')
|
|
Write('<dt>a\r\n')
|
|
Write('<dd>')
|
|
Write(tostring(a))
|
|
Write('\r\n')
|
|
Write('<dt>b\r\n')
|
|
Write('<dd>')
|
|
Write(tostring(b))
|
|
Write('\r\n')
|
|
Write('<dt>c\r\n')
|
|
Write('<dd>')
|
|
Write(tostring(c))
|
|
Write('\r\n')
|
|
Write('<dt>d\r\n')
|
|
Write('<dd>')
|
|
Write(tostring(d))
|
|
Write('\r\n')
|
|
Write('</dl>\r\n')
|
|
|
|
Write('<h3>source code to this page</h3>\r\n')
|
|
Write('<pre>\r\n')
|
|
Write(EscapeHtml(LoadAsset(GetEffectivePath())))
|
|
Write('</pre>\r\n')
|
|
|
|
-- redbean zip assets
|
|
Write('<h3>zip assets</h3>\r\n')
|
|
paths = GetZipPaths()
|
|
if #paths > 0 then
|
|
Write('<ul>\r\n')
|
|
for i = 1,#paths do
|
|
Write('<li>\r\n')
|
|
Write('<a href="')
|
|
Write(EscapeHtml(EscapePath(paths[i])))
|
|
Write('">')
|
|
Write(EscapeHtml(VisualizeControlCodes(paths[i])))
|
|
Write('</a>')
|
|
if IsHiddenPath(paths[i]) then
|
|
Write(' <small>[HIDDEN]</small>')
|
|
end
|
|
if not IsAcceptablePath(paths[i]) then
|
|
Write(' <small>[BLOCKED]</small>')
|
|
end
|
|
if not IsCompressed(paths[i]) then
|
|
Write(' <small>[UNCOMPRESSED]</small>')
|
|
end
|
|
if (GetAssetMode(paths[i]) & 0xF000) == 0x4000 then
|
|
Write(' <small>[DIRECTORY]</small>')
|
|
end
|
|
Write('<br>\r\n')
|
|
Write('Modified: ')
|
|
Write(FormatHttpDateTime(GetLastModifiedTime(paths[i])))
|
|
Write('<br>\r\n')
|
|
Write('Mode: ')
|
|
Write(string.format("0%o", GetAssetMode(paths[i])))
|
|
Write('<br>\r\n')
|
|
Write('Size: ')
|
|
Write(tostring(GetAssetSize(paths[i])))
|
|
Write('<br>\r\n')
|
|
if GetComment(paths[i]) then
|
|
Write('Comment: ')
|
|
Write(EscapeHtml(VisualizeControlCodes(GetComment(paths[i]))))
|
|
Write('<br>\r\n')
|
|
end
|
|
Write('\r\n')
|
|
end
|
|
Write('</ul>\r\n')
|
|
else
|
|
Write('<p><em>none</em>\r\n')
|
|
end
|
|
|
|
end
|
|
|
|
main()
|