add rc-httpd

This commit is contained in:
stanley lieber 2013-02-04 16:40:05 -06:00
parent 0b212ed505
commit c877493fb0
10 changed files with 616 additions and 0 deletions

54
rc/bin/rc-httpd/handlers/cgi Executable file
View file

@ -0,0 +1,54 @@
#!/bin/rc
fn filter_headers{
response='HTTP/1.1 200 OK'^$cr
lines=''
done=false
while(~ $done false){
line=`{read}
head=`{echo $line | awk '{print tolower($1)}'}
if(~ $head status:*){
tmp=`{echo $line | awk '{$1="" ; print}'}
response='HTTP/1.1 '^$"tmp^$cr
}
if not if(~ $line '')
done=true
if not
lines=$"lines^$"line^$cr^'
'
}
echo $response
echo -n $"lines
}
fn run_cgi {
path=$cgi_path exec $"cgi_bin $params
}
cgi_bin=$1
if(! ~ $cgi_bin /*){
pwd=`{pwd}
cgi_bin=$"pwd ^ / ^ $cgi_bin
}
cgi_dir=$*($#*)
if(! test -d $cgi_dir){
cgi_dir=`{basename -d $cgi_dir}
cgi_dir=$"cgi_dir
}
if(! test -d $"cgi_dir){
error 500
exit
}
if(! test -f $cgi_bin -x $cgi_bin){
error 500
exit
}
do_log 200
builtin cd $"cgi_dir
run_cgi | {
filter_headers
emit_extra_headers
echo $cr
exec cat
}

View file

@ -0,0 +1,111 @@
#!/bin/rc
PATH_INFO=`{echo $PATH_INFO | urlencode -d}
full_path=$"FS_ROOT^$"PATH_INFO
full_path=$"full_path
if(! test -d $full_path){
error 404
exit
}
if(! test -r $full_path -x $full_path){
error 503
exit
}
do_log 200
builtin cd $full_path
if(~ $"NOINDEXFILE ^ $"NOINDEX ''){
ifile=index.htm*
if(! ~ $ifile(1) *'*'){
PATH_INFO=$ifile(1)
FS_ROOT=''
exec serve-static
}
}
title=`{echo $SITE_TITLE | sed s,%s,^$"PATH_INFO^,}
title=$"title
lso=()
switch($2){
case size
# ls has no option to sort by size
# could pipe it through sort, I suppose
case date
lso=-t
}
echo 'HTTP/1.1 200 OK'^$cr
emit_extra_headers
echo 'Content-type: text/html'^$cr
echo $cr
echo '<html>
<head>
<title>'^$title^'</title>
<style type="text/css">
.size {
text-align: right;
padding-right: 4pt;
}
.day {
text-align: right;
padding-right: 3pt;
}
.datetime {
text-align: right;
}
.name {
text-align: right;
padding-left: 3pt;
}
</style>
</head>
<body>'
echo '<h1>'^$title^'</h1>'
if(! ~ $PATH_INFO /)
echo '<a href="../">Parent directory</a>'
echo '<table>'
ls -lQ $lso | awk '
function urlencode(loc){
# very minimal encoding, just enough for our static-file purposes
url=loc
gsub("%", "%25", url) # this one first!
gsub("\\$", "%24", url)
gsub("&", "%26", url)
gsub("\\+", "%2B", url)
gsub("\\?", "%3F", url)
gsub(" ", "%20", url)
gsub("\"", "%22", url)
gsub("#", "%23", url)
return url
}
function hrsize(size){
if(size > 1073741824) return sprintf("%.1fGB", size/1073741824)
if(size > 10485760) return sprintf("%iMB", size/1048576)
if(size > 1048576) return sprintf("%.1fMB", size/1048576)
if(size > 10240) return sprintf("%iKB", size/1024)
if(size > 1024) return sprintf("%.1fKB", size/1024)
return sprintf("%iB", size)
}
/^-/ {
print "<tr>"
print "<td class=\"size\">"hrsize($6)"</td>"
print "<td class=\"month\">"$7"</td>"
print "<td class=\"day\">"$8"</td>"
print "<td class=\"datetime\">"$9"</td>"
$1="" ; $2="" ; $3="" ; $4="" ; $5="" ; $6="" ; $7="" ; $8="" ; $9=""
sub("^ *?", "")
print "<td><a class=\"file name\" href=\""urlencode($0)"\">"$0"</a></td>"
print "</tr>"
$0=""
}
/^d/ {
print "<tr>"
print "<td class=\"size\"> </td>"
print "<td class=\"month\">"$7"</td>"
print "<td class=\"day\">"$8"</td>"
print "<td class=\"datetime\">"$9"</td>"
$1="" ; $2="" ; $3="" ; $4="" ; $5="" ; $6="" ; $7="" ; $8="" ; $9=""
sub("^ *?", "")
print "<td><a class=\"dir name\" href=\""urlencode($0)"/\">"$0"/</a></td>"
print "</tr>"
}'
echo '</table>
</body>
</html>'

38
rc/bin/rc-httpd/handlers/error Executable file
View file

@ -0,0 +1,38 @@
#!/bin/rc
# DO NOT make this script callable directly from the web!
fn do_error{
echo 'HTTP/1.1 '^$1^$cr
emit_extra_headers
echo 'Content-type: text/html'^$cr
echo $cr
echo '<html>
<head>
<title>'^$1^'</title>
</head>
<body>
<h1>'^$1^'</h1>'
echo $2
echo '<p><i>rc-httpd at' $SERVER_NAME '</i>'
echo '
</body>
</html>
'
}
fn 404{
do_error '404 Not Found' \
'The requested path '^$"location^' was not found on this server.'
}
fn 500{
do_error '500 Internal Server Error' \
'The server has encountered an internal misconfiguration and is unable to satisfy your request.'
}
fn 503{
do_error '503 Forbidden' \
'You do not have permission to access '^$"location^' on this server.'
}
do_log $1
$1

View file

@ -0,0 +1,30 @@
#!/bin/rc
if(~ $#2 0){
error 500
exit
}
switch($1){
case perm*
do_log 301
echo 'HTTP/1.1 301 Moved Permanently'^$cr
case temp*
do_log 302
echo 'HTTP/1.1 302 Moved Temporarily'^$cr
case seeother
do_log 303
echo 'HTTP/1.1 303 See Other'^$cr
case *
error 500
exit
}
echo 'Location: ' ^ $2 ^ $cr
emit_extra_headers
echo 'Content-type: text/html'^$cr
echo $cr
echo '<html><body>'
if(~ $#3 0)
echo 'Browser did not accept redirect.'
if not
echo $3
echo '<a href="'^$"location^'/">Click here</a>'
echo '</body></html>'

View file

@ -0,0 +1,30 @@
#!/bin/rc
full_path=`{echo $"FS_ROOT^$"PATH_INFO | urlencode -d}
full_path=$"full_path
if(~ $full_path */)
error 503
if(test -d $full_path){
redirect perm $"location^'/' \
'URL not quite right, and browser did not accept redirect.'
exit
}
if(! test -e $full_path){
error 404
exit
}
if(! test -r $full_path){
error 503
exit
}
do_log 200
type=`{file -m $full_path}
if(~ $type text/*)
max_age=3600 # 1 hour
if not
max_age=604800 # 1 week
echo 'HTTP/1.1 200 OK'^$cr
emit_extra_headers
echo 'Content-type: '^$type^$cr
echo 'Cache-control: max-age='^$max_age^$cr
echo $cr
exec cat $full_path

View file

@ -0,0 +1,14 @@
#!/bin/rc
cgiargs=$*
fn error{
if(~ $1 404)
exec cgi $cgiargs
if not
$rc_httpd_dir/handlers/error $1
}
if(~ $location */)
exec cgi $cgiargs
if not
exec serve-static

View file

@ -0,0 +1,5 @@
#!/bin/rc
if(~ $PATH_INFO */)
exec dir-index $params
if not
exec serve-static

86
rc/bin/rc-httpd/rc-httpd Executable file
View file

@ -0,0 +1,86 @@
#!/bin/rc
rc_httpd_dir=/rc/bin/rc-httpd
path=(/bin $rc_httpd_dir/handlers)
cgi_path=/bin
SERVER_PORT=80 # default for CGI scripts, may be overridden by the Host header
extra_headers='Server: rc-httpd'
cr=
fn do_log{
echo `{date} :: $SERVER_NAME :: $request :: \
$HTTP_USER_AGENT :: $1 :: $HTTP_REFERER >[1=2]
}
fn emit_extra_headers{
for(header in $extra_headers)
echo $"header^$cr
}
fn getline{ read | sed 's/'^$"cr^'$//g' }
fn terminate{
echo `{date} connection terminated >[1=2]
exit terminate
}
fn trim_input{ read -c $CONTENT_LENGTH }
request=`{getline}
if(~ $#request 0)
terminate
REQUEST_METHOD=$request(1)
REQUEST_URI=$request(2)
reqlines=''
HTTP_COOKIE=''
done=false
while(~ $"done false){
line=`{getline}
if(~ $#line 0)
done=true
reqlines=$"reqlines$"line'
'
h=`{echo $line | awk '{print tolower($1)}'}
switch($h){
case ''
done=true
case host:
tmp=`{echo $line(2) | sed 's/:/ /'}
SERVER_NAME=$tmp(1)
if(! ~ $#tmp 1)
SERVER_PORT=$tmp(2)
case referer:
HTTP_REFERER=$line(2)
case user-agent:
HTTP_USER_AGENT=`{echo $line | sed 's;[^:]+:[ ]+;;'}
case content-length:
CONTENT_LENGTH=$line(2)
case cookie:
cookie=`{echo $line | sed 's;^[^:]+:[ ]*;;'}
HTTP_COOKIE=$"HTTP_COOKIE^$"cookie^'; '
}
}
if(~ $REQUEST_URI http://*){
SERVER_NAME=`{echo $REQUEST_URI | sed '
s;^http://;;
s;/.*;;
'}
REQUEST_URI=`{echo $REQUEST_URI | sed 's;^http://[^/]+/?;/;'}
}
QUERY_STRING=`{echo $REQUEST_URI | sed 's;[^?]*\??;;'}
params=`{echo $QUERY_STRING | sed 's;\+; ;g'}
location=`{echo $REQUEST_URI | sed 's;\?.*;;'}
location=`{echo $location | sed '
s;[^/]+/\.\./;/;g
s;/\./;/;g
s;//+;/;g
'}
if(~ $REQUEST_METHOD POST){
if(! ~ $"CONTENT_LENGTH '')
trim_input | exec $rc_httpd_dir/select-handler
if not{
echo 'POST without content-length, assuming no keep-alive.' >[1=2]
exec $rc_httpd_dir/select-handler
}
}
if not
. $rc_httpd_dir/select-handler

71
sys/lib/dist/rc/bin/rc-httpd/select-handler vendored Executable file
View file

@ -0,0 +1,71 @@
#!/bin/rc
PATH_INFO=$location
FS_ROOT=/sys/doc
exec static-or-index
## EXAMPLES
#
#SERVER_NAME=`{echo $SERVER_NAME | sed 's/^www\.//g'}
#
#fn do_error{
# do_log $1
# echo 'HTTP/1.1 '^$1^$cr
# emit_extra_headers
# echo 'Content-type: text/html'^$cr
# echo $cr
# echo '<html>
#<head>
#<title>'^$1^'</title>
#</head>
#<body>
#<h1>'^$1^'</h1>'
# echo $2
# echo '<p><i>rc-httpd at' $SERVER_NAME '</i>'
# echo '
# </body>
# </html>
# '
#}
#
## surprise!
#if(~ $HTTP_REFERER *hiphopstan.com/forum* *slax.*/forum*){
# PATH_INFO=$location
# FS_ROOT=/usr/sl/www/werc/sites/hotlink
# exec static-or-index
#}
#if(~ $HTTP_REFERER 'http://okturing.com/index.rc?start=100' || {~ $SERVER_NAME okturing.com && ~ $location /index.rc} || ~ $location /qemu/plan9.flp.gz){
# do_error '27b/6'
# exit
#}
#
## sites
#if(~ $SERVER_NAME 1oct1993.com 9front.org *.9front.org emma.stanleylieber.com flamesgif.com gl.* iawtp.com inri.net massivefictions.com mold.dk osx.* other.* pop.* qualitycountrylyrics.com sp.* stanleylieber.com tcasey.* textadventure.* volksutils.com){
# PATH_INFO=$location
# FS_ROOT=/usr/sl/www/werc/sites/$SERVER_NAME
# exec static-or-index
#}
#if not if(~ $SERVER_NAME 9front.bell-labs.co bell-labs.co cs.bell-labs.co plan9.bell-labs.co sources.cs.bell-labs.co){
# PATH_INFO=$location
# FS_ROOT=/usr/sl/www/werc/sites/bell-labs.co
# exec static-or-index
#}
#if not if(~ $SERVER_NAME vr.stanleylieber.com){
# if(~ $location / /bin/* /etc/* /*htaccess /*htpasswd /index.rc* /lib/* /stats/*){
# PATH_INFO=$location
# FS_ROOT=/usr/sl/www/werc/sitesvr.stanleylieber.com
# exec cgi /usr/sl/www/werc/sites/vr.stanleylieber.com/index.rc $*
# }
# if not{
# PATH_INFO=$location
# FS_ROOT=/usr/sl/www/werc/sites/vr.stanleylieber.com
# exec static-or-index
# }
#}
#if not if(~ $SERVER_NAME applied.bell-labs.co *cat-v.org flesh.* img.* linux.* notreally.info okturing.com openbsd.* *osuny.co.uk plan9.* read.* scandisk.bell-labs.co * url.*){
# PATH_INFO=$location
# FS_ROOT=/usr/sl/www/werc/sites/$SERVER_NAME
# exec static-or-cgi /usr/sl/www/werc/bin/werc.rc
#}
#if not
# error 503

177
sys/man/8/rc-httpd Normal file
View file

@ -0,0 +1,177 @@
.TH RC-HTTPD 8
.SH NAME
rc-httpd \- HTTP server
.SH SYNOPSIS
.B rc-httpd/rc-httpd
.SH DESCRIPTION
.I Rc-httpd
serves the requested file or an index of files found under
a website's root directory, or, in the case of CGI, executes
a specified CGI program.
.SH CONFIGURATION
.PP
As all pieces of
.B rc-httpd
are shell scripts, configuration is achieved by setting variables
and adding, removing or modifying commands in various files.
.PP
.B rc-httpd
.PP
.I rc_httpd_dir
must be set to the root of the rc-httpd installation,
the directory containing the rc-httpd script.
.PP
.I path
must include
.I rc_httpd_dir/handlers
ahead of the base system's path elements.
.PP
.I cgi_path
is substituted for
.I path
when cgi scripts are run. (Be sure
to set
.I path
back in rc-based cgi scripts.)
.PP
.I extra_headers
is an optional list of strings to emit when sending http headers.
.PP
.I SERVER_PORT
is the port HTTP is to be served on.
.PP
.B select-handler
.PP
.I PATH_INFO
is the location relative to the website's root directory of the file
to be displayed.
Typically, the
.I location
from the incoming request is honored.
.PP
.I FS_ROOT
sets the root directory of the website.
.PP
.I NOINDEXFILE
instructs the
.B dir-index
module not to
look for
.B index.html
files, otherwise if an
.B index.html
file is found
.B dir-index
will exec
.B serve-static
to serve the file. At present there
is no module to serve an index file but not a directory.
.PP
If you do not want directory indexing at all, replace
.B static-or-index
with
.B serve-static,
which will report 503 forbidden for directories.
.PP
Multiple virtual hosts may be configured by creating conditional
statements that act upon the
.I SERVER_NAME
variable. Fine-grained control of specific request strings may
be configured via a similar method acting upon the
.I location
and/or other variables.
.SH EXAMPLES
The following examples demonstrate possible ways to configure
.BR select-handler.
.PP
Serve static files:
.RS
.EX
if(~ $SERVER_NAME 9front.org){
PATH_INFO=$location
FS_ROOT=/usr/sl/www/$SERVER_NAME
exec static-or-index
}
.EE
.RE
.PP
CGI:
.RS
.EX
if(~ $SERVER_NAME *cat-v.org){
PATH_INFO=$location
FS_ROOT=/usr/sl/www/werc/sites/$SERVER_NAME
exec static-or-cgi /usr/sl/www/werc/bin/werc.rc
}
.EE
.RE
.PP
Custom error message for a denied URL:
.RS
.EX
fn do_error{
do_log $1
echo 'HTTP/1.1 '^$1^$cr
emit_extra_headers
echo 'Content-type: text/html'^$cr
echo $cr
echo '<html>
<head>
<title>'^$1^'</title>
</head>
<body>
<h1>'^$1^'</h1>'
echo $2
echo '<p><i>rc-httpd at' $SERVER_NAME '</i>'
echo '
</body>
</html>
'
}
if(~ $location /v8.tar.bz2){
do_error '27b/6'
exit
}
.EE
.RE
.SH STARTUP
.I Rc-httpd
is run from a file in the directory scanned by
.IR listen (8),
or called as an argument to
.IR listen1 (8).
The program's standard error may be captured to a log file:
.RS
.EX
exec /rc/bin/rc-httpd/rc-httpd >>[2]/sys/log/www
.EE
.RE
.SH FILES
.TF /sys/lib/httpd.rewrite
.TP
.B /rc/bin/rc-httpd/rc-httpd
.TP
.B /rc/bin/rc-httpd/select-handlers
.TP
.B /rc/bin/rc-httpd/handlers/cgi
.TP
.B /rc/bin/rc-httpd/handlers/dir-index
.TP
.B /rc/bin/rc-httpd/handlers/error
.TP
.B /rc/bin/rc-httpd/handlers/redirect
.TP
.B /rc/bin/rc-httpd/handlers/serve-static
.TP
.B /rc/bin/rc-httpd/handlers/static-or-cgi
.TP
.B /rc/bin/rc-httpd/handlers/static-or-index
.TP
.B /rc/bin/service/tcp80
.TP
.B /sys/log/www
.SH SOURCE
.B /rc/bin/rc-httpd
.SH "SEE ALSO"
.IR rc (1),
.IR listen (8)