[Python-checkins] CVS: python/dist/src/Lib CGIHTTPServer.py,1.13,1.14
Guido van Rossum
python-dev@python.org
Mon, 18 Sep 2000 21:01:05 -0700
Update of /cvsroot/python/python/dist/src/Lib
In directory slayer.i.sourceforge.net:/tmp/cvs-serv4667
Modified Files:
CGIHTTPServer.py
Log Message:
An honest attempt to make this work on Unix, Windows, and even
Macintosh (the latter untested).
This closes Bug #110839.
Index: CGIHTTPServer.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/CGIHTTPServer.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -r1.13 -r1.14
*** CGIHTTPServer.py 2000/09/01 03:27:34 1.13
--- CGIHTTPServer.py 2000/09/19 04:01:01 1.14
***************
*** 4,17 ****
requests to cgi-bin scripts.
! If the os.fork() function is not present, this module will not work;
! SystemError will be raised instead.
"""
! __version__ = "0.3"
import os
import string
import urllib
--- 4,26 ----
requests to cgi-bin scripts.
! If the os.fork() function is not present (e.g. on Windows),
! os.popen2() is used as a fallback, with slightly altered semantics; if
! that function is not present either (e.g. on Macintosh), only Python
! scripts are supported, and they are executed by the current process.
+ In all cases, the implementation is intentionally naive -- all
+ requests are executed sychronously.
+
+ SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
+ -- it may execute arbitrary Python code or external programs.
+
"""
! __version__ = "0.4"
import os
+ import sys
import string
import urllib
***************
*** 20,29 ****
- try:
- os.fork
- except AttributeError:
- raise SystemError, __name__ + " requires os.fork()"
-
-
class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
--- 29,32 ----
***************
*** 36,39 ****
--- 39,46 ----
"""
+ # Determine platform specifics
+ have_fork = hasattr(os, 'fork')
+ have_popen2 = hasattr(os, 'popen2')
+
# Make rfile unbuffered -- we need to read one line and then pass
# the rest to a subprocess, so we can't use buffered input.
***************
*** 60,66 ****
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.
--- 67,73 ----
def is_cgi(self):
! """Test whether self.path corresponds to a CGI script.
! Return a tuple (dir, rest) if self.path requires running a
CGI script, None if not. Note that rest begins with a
slash if it is not empty.
***************
*** 84,87 ****
--- 91,103 ----
cgi_directories = ['/cgi-bin', '/htbin']
+ def is_executable(self, path):
+ """Test whether argument path is an executable file."""
+ return executable(path)
+
+ def is_python(self, path):
+ """Test whether argument path is a Python script."""
+ head, tail = os.path.splitext(path)
+ return tail.lower() in (".py", ".pyw")
+
def run_cgi(self):
"""Execute a CGI script."""
***************
*** 105,182 ****
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
! if self.headers.typeheader is None:
! env['CONTENT_TYPE'] = self.headers.type
else:
! env['CONTENT_TYPE'] = self.headers.typeheader
! 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
! co = filter(None, self.headers.getheaders('cookie'))
! if co:
! env['HTTP_COOKIE'] = string.join(co, ', ')
! # XXX Other HTTP_* headers
! decoded_query = string.replace(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)
--- 121,271 ----
self.send_error(403, "CGI script is not a plain file (%s)" %
`scriptname`)
return
! ispy = self.is_python(scriptname)
! if not ispy:
! if not (self.have_fork or self.have_popen2):
! self.send_error(403, "CGI script is not a Python script (%s)" %
! `scriptname`)
! return
! if not self.is_executable(scriptfile):
! self.send_error(403, "CGI script is not executable (%s)" %
! `scriptname`)
! return
!
! # 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]
! # XXX AUTH_TYPE
! # XXX REMOTE_USER
! # XXX REMOTE_IDENT
! if self.headers.typeheader is None:
! env['CONTENT_TYPE'] = self.headers.type
! else:
! env['CONTENT_TYPE'] = self.headers.typeheader
! 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
! co = filter(None, self.headers.getheaders('cookie'))
! if co:
! env['HTTP_COOKIE'] = string.join(co, ', ')
! # XXX Other HTTP_* headers
! if not self.have_fork:
! # Since we're setting the env in the parent, provide empty
! # values to override previously set values
! for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
! 'HTTP_USER_AGENT', 'HTTP_COOKIE'):
! env.setdefault(k, "")
!
self.send_response(200, "Script output follows")
!
! decoded_query = string.replace(query, '+', ' ')
!
! if self.have_fork:
! # Unix -- fork as we should
! args = [script]
! if '=' not in decoded_query:
! args.append(decoded_query)
! nobody = nobody_uid()
! 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", sts)
! return
! # Child
! try:
! try:
! os.setuid(nobody)
! except os.error:
! pass
! os.dup2(self.rfile.fileno(), 0)
! os.dup2(self.wfile.fileno(), 1)
! os.execve(scriptfile, args, env)
! except:
! self.server.handle_error(self.request, self.client_address)
! os._exit(127)
!
! elif self.have_popen2:
! # Windows -- use popen2 to create a subprocess
! import shutil
! os.environ.update(env)
! cmdline = scriptfile
! if self.is_python(scriptfile):
! interp = sys.executable
! if interp.lower().endswith("w.exe"):
! # On Windows, use python.exe, not python.exe
! interp = interp[:-5] = interp[-4:]
! cmdline = "%s %s" % (interp, cmdline)
! if '=' not in query and '"' not in query:
! cmdline = '%s "%s"' % (cmdline, query)
! self.log_error("command: %s", cmdline)
! try:
! nbytes = int(length)
! except:
! nbytes = 0
! fi, fo = os.popen2(cmdline)
! if self.command.lower() == "post" and nbytes > 0:
! data = self.rfile.read(nbytes)
! fi.write(data)
! fi.close()
! shutil.copyfileobj(fo, self.wfile)
! sts = fo.close()
if sts:
! self.log_error("CGI script exit status %#x", sts)
else:
! self.log_error("CGI script exited OK")
!
! else:
! # Other O.S. -- execute script in this process
! os.environ.update(env)
! save_argv = sys.argv
! save_stdin = sys.stdin
! save_stdout = sys.stdout
! save_stderr = sys.stderr
try:
! try:
! sys.argv = [scriptfile]
! if '=' not in decoded_query:
! sys.argv.append(decoded_query)
! sys.stdout = self.wfile
! sys.stdin = self.rfile
! execfile(scriptfile, {"__name__": "__main__"})
! finally:
! sys.argv = save_argv
! sys.stdin = save_stdin
! sys.stdout = save_stdout
! sys.stderr = save_stderr
! except SystemExit, sts:
! self.log_error("CGI script exit status %s", str(sts))
! else:
! self.log_error("CGI script exited OK")
***************
*** 188,192 ****
if nobody:
return nobody
! import pwd
try:
nobody = pwd.getpwnam('nobody')[2]
--- 277,284 ----
if nobody:
return nobody
! try:
! import pwd
! except ImportError:
! return -1
try:
nobody = pwd.getpwnam('nobody')[2]