shithub: riscv

Download patch

ref: c877493fb00717de1a22860d88efb75ebf14ebec
parent: 0b212ed5054cd08bbc14a12936c07bc1ff5890a2
author: stanley lieber <[email protected]>
date: Mon Feb 4 11:40:05 EST 2013

add rc-httpd

--- /dev/null
+++ b/rc/bin/rc-httpd/handlers/cgi
@@ -1,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
+}
--- /dev/null
+++ b/rc/bin/rc-httpd/handlers/dir-index
@@ -1,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>'
--- /dev/null
+++ b/rc/bin/rc-httpd/handlers/error
@@ -1,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
--- /dev/null
+++ b/rc/bin/rc-httpd/handlers/redirect
@@ -1,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>'
--- /dev/null
+++ b/rc/bin/rc-httpd/handlers/serve-static
@@ -1,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
--- /dev/null
+++ b/rc/bin/rc-httpd/handlers/static-or-cgi
@@ -1,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
--- /dev/null
+++ b/rc/bin/rc-httpd/handlers/static-or-index
@@ -1,0 +1,5 @@
+#!/bin/rc
+if(~ $PATH_INFO */)
+	exec dir-index $params
+if not
+	exec serve-static
--- /dev/null
+++ b/rc/bin/rc-httpd/rc-httpd
@@ -1,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
--- /dev/null
+++ b/sys/lib/dist/rc/bin/rc-httpd/select-handler
@@ -1,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
--- /dev/null
+++ b/sys/man/8/rc-httpd
@@ -1,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)