1 """CGI-savvy HTTP Server.
3 This module builds on SimpleHTTPServer by implementing GET and POST
4 requests to cgi-bin scripts.
6 If the os.fork() function is not present (e.g. on Windows),
7 os.popen2() is used as a fallback, with slightly altered semantics; if
8 that function is not present either (e.g. on Macintosh), only Python
9 scripts are supported, and they are executed by the current process.
11 In all cases, the implementation is intentionally naive -- all
12 requests are executed sychronously.
14 SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
15 -- it may execute arbitrary Python code or external programs.
22 __all__ = [
"CGIHTTPRequestHandler"]
28 import SimpleHTTPServer
33 """Complete HTTP server with GET, HEAD and POST commands.
35 GET and HEAD also support running CGI scripts.
37 The POST command is *only* implemented for CGI scripts.
42 have_fork = hasattr(os,
'fork')
43 have_popen2 = hasattr(os,
'popen2')
44 have_popen3 = hasattr(os,
'popen3')
51 """Serve a POST request.
53 This is only implemented for CGI scripts.
60 self.
send_error(501,
"Can only POST to CGI scripts")
63 """Version of send_head that support CGI scripts"""
70 """Test whether self.path corresponds to a CGI script.
72 Return a tuple (dir, rest) if self.path requires running a
73 CGI script, None if not. Note that rest begins with a
74 slash if it is not empty.
76 The default implementation tests whether the path
77 begins with one of the strings in the list
78 self.cgi_directories (and the next character is a '/'
79 or the end of the string).
87 if path[:i] == x
and (
not path[i:]
or path[i] ==
'/'):
92 cgi_directories = [
'/cgi-bin',
'/htbin']
95 """Test whether argument path is an executable file."""
99 """Test whether argument path is a Python script."""
100 head, tail = os.path.splitext(path)
101 return tail.lower()
in (
".py",
".pyw")
104 """Execute a CGI script."""
108 rest, query = rest[:i], rest[i+1:]
113 script, rest = rest[:i], rest[i:]
115 script, rest = rest,
''
116 scriptname = dir +
'/' + script
118 if not os.path.exists(scriptfile):
119 self.
send_error(404,
"No such CGI script (%s)" % `scriptname`)
121 if not os.path.isfile(scriptfile):
122 self.
send_error(403,
"CGI script is not a plain file (%s)" %
128 self.
send_error(403,
"CGI script is not a Python script (%s)" %
132 self.
send_error(403,
"CGI script is not executable (%s)" %
140 env[
'SERVER_NAME'] = self.server.server_name
141 env[
'GATEWAY_INTERFACE'] =
'CGI/1.1'
143 env[
'SERVER_PORT'] =
str(self.server.server_port)
144 env[
'REQUEST_METHOD'] = self.command
146 env[
'PATH_INFO'] = uqrest
148 env[
'SCRIPT_NAME'] = scriptname
150 env[
'QUERY_STRING'] = query
153 env[
'REMOTE_HOST'] = host
158 if self.headers.typeheader
is None:
159 env[
'CONTENT_TYPE'] = self.headers.type
161 env[
'CONTENT_TYPE'] = self.headers.typeheader
162 length = self.headers.getheader(
'content-length')
164 env[
'CONTENT_LENGTH'] = length
166 for line
in self.headers.getallmatchingheaders(
'accept'):
167 if line[:1]
in "\t\n\r ":
168 accept.append(line.strip())
170 accept = accept + line[7:].
split(
',')
171 env[
'HTTP_ACCEPT'] =
','.
join(accept)
172 ua = self.headers.getheader(
'user-agent')
174 env[
'HTTP_USER_AGENT'] = ua
175 co =
filter(
None, self.headers.getheaders(
'cookie'))
177 env[
'HTTP_COOKIE'] =
', '.
join(co)
182 for k
in (
'QUERY_STRING',
'REMOTE_HOST',
'CONTENT_LENGTH',
183 'HTTP_USER_AGENT',
'HTTP_COOKIE'):
184 env.setdefault(k,
"")
188 decoded_query = query.replace(
'+',
' ')
193 if '=' not in decoded_query:
194 args.append(decoded_query)
201 pid, sts = os.waitpid(pid, 0)
203 self.
log_error(
"CGI script exit status %#x", sts)
211 os.dup2(self.rfile.fileno(), 0)
212 os.dup2(self.wfile.fileno(), 1)
213 os.execve(scriptfile, args, env)
225 os.environ.update(env)
228 interp = sys.executable
229 if interp.lower().endswith(
"w.exe"):
231 interp = interp[:-5] + interp[-4:]
232 cmdline =
"%s -u %s" % (interp, cmdline)
233 if '=' not in query
and '"' not in query:
234 cmdline =
'%s "%s"' % (cmdline, query)
240 files = popenx(cmdline,
'b')
245 if self.command.lower() ==
"post" and nbytes > 0:
246 data = self.rfile.read(nbytes)
257 self.
log_error(
"CGI script exit status %#x", sts)
263 os.environ.update(env)
265 save_stdin = sys.stdin
266 save_stdout = sys.stdout
267 save_stderr = sys.stderr
270 sys.argv = [scriptfile]
271 if '=' not in decoded_query:
272 sys.argv.append(decoded_query)
273 sys.stdout = self.
wfile
274 sys.stdin = self.
rfile
275 execfile(scriptfile, {
"__name__":
"__main__"})
278 sys.stdin = save_stdin
279 sys.stdout = save_stdout
280 sys.stderr = save_stderr
281 except SystemExit, sts:
290 """Internal routine to get nobody's uid"""
299 nobody = pwd.getpwnam(
'nobody')[2]
301 nobody = 1 +
max(map(
lambda x: x[2], pwd.getpwall()))
306 """Test for executable file."""
311 return st[0] & 0111 != 0
314 def test(HandlerClass = CGIHTTPRequestHandler,
319 if __name__ ==
'__main__':