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, this module will not work;
7 SystemError will be raised instead.
19 import SimpleHTTPServer
24 except AttributeError:
25 raise SystemError, __name__
+ " requires os.fork()"
28 class CGIHTTPRequestHandler(SimpleHTTPServer
.SimpleHTTPRequestHandler
):
30 """Complete HTTP server with GET, HEAD and POST commands.
32 GET and HEAD also support running CGI scripts.
34 The POST command is *only* implemented for CGI scripts.
39 """Serve a POST request.
41 This is only implemented for CGI scripts.
48 self
.send_error(501, "Can only POST to CGI scripts")
51 """Version of send_head that support CGI scripts"""
55 return SimpleHTTPServer
.SimpleHTTPRequestHandler
.send_head(self
)
58 """test whether PATH corresponds to a CGI script.
60 Return a tuple (dir, rest) if PATH requires running a
61 CGI script, None if not. Note that rest begins with a
62 slash if it is not empty.
64 The default implementation tests whether the path
65 begins with one of the strings in the list
66 self.cgi_directories (and the next character is a '/'
67 or the end of the string).
73 for x
in self
.cgi_directories
:
75 if path
[:i
] == x
and (not path
[i
:] or path
[i
] == '/'):
76 self
.cgi_info
= path
[:i
], path
[i
+1:]
80 cgi_directories
= ['/cgi-bin', '/htbin']
83 """Execute a CGI script."""
84 dir, rest
= self
.cgi_info
85 i
= string
.rfind(rest
, '?')
87 rest
, query
= rest
[:i
], rest
[i
+1:]
90 i
= string
.find(rest
, '/')
92 script
, rest
= rest
[:i
], rest
[i
:]
94 script
, rest
= rest
, ''
95 scriptname
= dir + '/' + script
96 scriptfile
= self
.translate_path(scriptname
)
97 if not os
.path
.exists(scriptfile
):
98 self
.send_error(404, "No such CGI script (%s)" % `scriptname`
)
100 if not os
.path
.isfile(scriptfile
):
101 self
.send_error(403, "CGI script is not a plain file (%s)" %
104 if not executable(scriptfile
):
105 self
.send_error(403, "CGI script is not executable (%s)" %
108 nobody
= nobody_uid()
109 self
.send_response(200, "Script output follows")
110 self
.wfile
.flush() # Always flush before forking
114 pid
, sts
= os
.waitpid(pid
, 0)
116 self
.log_error("CGI script exit status x%x" % sts
)
120 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
121 # XXX Much of the following could be prepared ahead of time!
123 env
['SERVER_SOFTWARE'] = self
.version_string()
124 env
['SERVER_NAME'] = self
.server
.server_name
125 env
['GATEWAY_INTERFACE'] = 'CGI/1.1'
126 env
['SERVER_PROTOCOL'] = self
.protocol_version
127 env
['SERVER_PORT'] = str(self
.server
.server_port
)
128 env
['REQUEST_METHOD'] = self
.command
129 uqrest
= urllib
.unquote(rest
)
130 env
['PATH_INFO'] = uqrest
131 env
['PATH_TRANSLATED'] = self
.translate_path(uqrest
)
132 env
['SCRIPT_NAME'] = scriptname
134 env
['QUERY_STRING'] = query
135 host
= self
.address_string()
136 if host
!= self
.client_address
[0]:
137 env
['REMOTE_HOST'] = host
138 env
['REMOTE_ADDR'] = self
.client_address
[0]
142 if self
.headers
.typeheader
is None:
143 env
['CONTENT_TYPE'] = self
.headers
.type
145 env
['CONTENT_TYPE'] = self
.headers
.typeheader
146 length
= self
.headers
.getheader('content-length')
148 env
['CONTENT_LENGTH'] = length
150 for line
in self
.headers
.getallmatchingheaders('accept'):
151 if line
[:1] in string
.whitespace
:
152 accept
.append(string
.strip(line
))
154 accept
= accept
+ string
.split(line
[7:], ',')
155 env
['HTTP_ACCEPT'] = string
.joinfields(accept
, ',')
156 ua
= self
.headers
.getheader('user-agent')
158 env
['HTTP_USER_AGENT'] = ua
159 co
= filter(None, self
.headers
.getheaders('cookie'))
161 env
['HTTP_COOKIE'] = string
.join(co
, ', ')
162 # XXX Other HTTP_* headers
163 decoded_query
= string
.replace(query
, '+', ' ')
168 os
.dup2(self
.rfile
.fileno(), 0)
169 os
.dup2(self
.wfile
.fileno(), 1)
170 print scriptfile
, script
, decoded_query
171 os
.execve(scriptfile
,
172 [script
, decoded_query
],
175 self
.server
.handle_error(self
.request
, self
.client_address
)
182 """Internal routine to get nobody's uid"""
188 nobody
= pwd
.getpwnam('nobody')[2]
190 nobody
= 1 + max(map(lambda x
: x
[2], pwd
.getpwall()))
194 def executable(path
):
195 """Test for executable file."""
200 return st
[0] & 0111 != 0
203 def test(HandlerClass
= CGIHTTPRequestHandler
,
204 ServerClass
= BaseHTTPServer
.HTTPServer
):
205 SimpleHTTPServer
.test(HandlerClass
, ServerClass
)
208 if __name__
== '__main__':