summaryrefslogtreecommitdiffstats
path: root/Lib/CGIHTTPServer.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1995-08-04 04:00:20 (GMT)
committerGuido van Rossum <guido@python.org>1995-08-04 04:00:20 (GMT)
commite7e578ffe042ca84f5b57e8056cb5598c7b44e5a (patch)
tree4a3b2e705ae78d7030280aef3f5839579755a698 /Lib/CGIHTTPServer.py
parent40d1ea3b9ccf9e779f253685d1f5f6cf530945fa (diff)
downloadcpython-e7e578ffe042ca84f5b57e8056cb5598c7b44e5a.zip
cpython-e7e578ffe042ca84f5b57e8056cb5598c7b44e5a.tar.gz
cpython-e7e578ffe042ca84f5b57e8056cb5598c7b44e5a.tar.bz2
Initial revision
Diffstat (limited to 'Lib/CGIHTTPServer.py')
-rw-r--r--Lib/CGIHTTPServer.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/Lib/CGIHTTPServer.py b/Lib/CGIHTTPServer.py
new file mode 100644
index 0000000..eeb51fe
--- /dev/null
+++ b/Lib/CGIHTTPServer.py
@@ -0,0 +1,203 @@
+"""CGI-savvy HTTP Server.
+
+This module builds on SimpleHTTPServer by implementing GET and POST
+requests to cgi-bin scripts.
+
+"""
+
+
+__version__ = "0.2"
+
+
+import os
+import sys
+import time
+import socket
+import string
+import urllib
+import BaseHTTPServer
+import SimpleHTTPServer
+
+
+class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+ """Complete HTTP server with GET, HEAD and POST commands.
+
+ GET and HEAD also support running CGI scripts.
+
+ The POST command is *only* implemented for CGI scripts.
+
+ """
+
+ def do_POST(self):
+ """Serve a POST request.
+
+ This is only implemented for CGI scripts.
+
+ """
+
+ if self.is_cgi():
+ self.run_cgi()
+ else:
+ self.send_error(501, "Can only POST to CGI scripts")
+
+ def send_head(self):
+ """Version of send_head that support CGI scripts"""
+ if self.is_cgi():
+ return self.run_cgi()
+ else:
+ return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
+
+ def is_cgi(self):
+ """test whether PATH corresponds to a CGI script.
+
+ Return a tuple (dir, rest) if PATH requires running a
+ CGI script, None if not. Note that rest begins with a
+ slash if it is not empty.
+
+ The default implementation tests whether the path
+ begins with one of the strings in the list
+ self.cgi_directories (and the next character is a '/'
+ or the end of the string).
+
+ """
+
+ path = self.path
+
+ for x in self.cgi_directories:
+ i = len(x)
+ if path[:i] == x and (not path[i:] or path[i] == '/'):
+ self.cgi_info = path[:i], path[i+1:]
+ return 1
+ return 0
+
+ cgi_directories = ['/cgi-bin', '/htbin']
+
+ def run_cgi(self):
+ """Execute a CGI script."""
+ dir, rest = self.cgi_info
+ i = string.rfind(rest, '?')
+ if i >= 0:
+ rest, query = rest[:i], rest[i+1:]
+ else:
+ query = ''
+ i = string.find(rest, '/')
+ if i >= 0:
+ script, rest = rest[:i], rest[i:]
+ else:
+ script, rest = rest, ''
+ scriptname = dir + '/' + script
+ scriptfile = self.translate_path(scriptname)
+ if not os.path.exists(scriptfile):
+ self.send_error(404, "No such CGI script (%s)", `scriptname`)
+ return
+ if not os.path.isfile(scriptfile):
+ self.send_error(403, "CGI script is not a plain file (%s)",
+ `scriptname`)
+ return
+ if not executable(scriptfile):
+ self.send_error(403, "CGI script is not executable (%s)",
+ `scriptname`)
+ return
+ nobody = nobody_uid()
+ self.send_response(200, "Script output follows")
+ self.wfile.flush() # Always flush before forking
+ pid = os.fork()
+ if pid != 0:
+ # Parent
+ pid, sts = os.waitpid(pid, 0)
+ if sts:
+ self.log_error("CGI script exit status x%x" % sts)
+ return
+ # Child
+ try:
+ # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
+ # XXX Much of the following could be prepared ahead of time!
+ env = {}
+ env['SERVER_SOFTWARE'] = self.version_string()
+ env['SERVER_NAME'] = self.server.server_name
+ env['GATEWAY_INTERFACE'] = 'CGI/1.1'
+ env['SERVER_PROTOCOL'] = self.protocol_version
+ env['SERVER_PORT'] = str(self.server.server_port)
+ env['REQUEST_METHOD'] = self.command
+ uqrest = urllib.unquote(rest)
+ env['PATH_INFO'] = uqrest
+ env['PATH_TRANSLATED'] = self.translate_path(uqrest)
+ env['SCRIPT_NAME'] = scriptname
+ if query:
+ env['QUERY_STRING'] = query
+ host = self.address_string()
+ if host != self.client_address[0]:
+ env['REMOTE_HOST'] = host
+ env['REMOTE_ADDR'] = self.client_address[0]
+ # AUTH_TYPE
+ # REMOTE_USER
+ # REMOTE_IDENT
+ env['CONTENT_TYPE'] = self.headers.type
+ length = self.headers.getheader('content-length')
+ if length:
+ env['CONTENT_LENGTH'] = length
+ accept = []
+ for line in self.headers.getallmatchingheaders('accept'):
+ if line[:1] in string.whitespace:
+ accept.append(string.strip(line))
+ else:
+ accept = accept + string.split(line[7:])
+ env['HTTP_ACCEPT'] = string.joinfields(accept, ',')
+ ua = self.headers.getheader('user-agent')
+ if ua:
+ env['HTTP_USER_AGENT'] = ua
+ # XXX Other HTTP_* headers
+ import regsub
+ decoded_query = regsub.gsub('+', ' ', query)
+ try:
+ os.setuid(nobody)
+ except os.error:
+ pass
+ os.dup2(self.rfile.fileno(), 0)
+ os.dup2(self.wfile.fileno(), 1)
+ print scriptfile, script, decoded_query
+ os.execve(scriptfile,
+ [script, decoded_query],
+ env)
+ except:
+ self.server.handle_error(self.request, self.client_address)
+ os._exit(127)
+
+
+nobody = None
+
+def nobody_uid():
+ """Internal routine to get nobody's uid"""
+ global nobody
+ if nobody:
+ return nobody
+ import pwd
+ try:
+ nobody = pwd.getpwnam('nobody')[2]
+ except pwd.error:
+ nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
+ return nobody
+
+
+def executable(path):
+ """Test for executable file."""
+ try:
+ st = os.stat(path)
+ except os.error:
+ return 0
+ return 1
+
+
+def test(HandlerClass = CGIHTTPRequestHandler,
+ ServerClass = BaseHTTPServer.HTTPServer):
+ import sys
+ if sys.argv[1:2] == ['-r']:
+ db = MyArchive()
+ db.regenindices()
+ return
+ SimpleHTTPServer.test(HandlerClass, ServerClass)
+
+
+if __name__ == '__main__':
+ test()