1 """CGI-savvy HTTP Server.
3 This module builds on SimpleHTTPServer by implementing GET and POST
4 requests to cgi-bin scripts.
19 import SimpleHTTPServer
22 class CGIHTTPRequestHandler(SimpleHTTPServer
.SimpleHTTPRequestHandler
):
24 """Complete HTTP server with GET, HEAD and POST commands.
26 GET and HEAD also support running CGI scripts.
28 The POST command is *only* implemented for CGI scripts.
33 """Serve a POST request.
35 This is only implemented for CGI scripts.
42 self
.send_error(501, "Can only POST to CGI scripts")
45 """Version of send_head that support CGI scripts"""
49 return SimpleHTTPServer
.SimpleHTTPRequestHandler
.send_head(self
)
52 """test whether PATH corresponds to a CGI script.
54 Return a tuple (dir, rest) if PATH requires running a
55 CGI script, None if not. Note that rest begins with a
56 slash if it is not empty.
58 The default implementation tests whether the path
59 begins with one of the strings in the list
60 self.cgi_directories (and the next character is a '/'
61 or the end of the string).
67 for x
in self
.cgi_directories
:
69 if path
[:i
] == x
and (not path
[i
:] or path
[i
] == '/'):
70 self
.cgi_info
= path
[:i
], path
[i
+1:]
74 cgi_directories
= ['/cgi-bin', '/htbin']
77 """Execute a CGI script."""
78 dir, rest
= self
.cgi_info
79 i
= string
.rfind(rest
, '?')
81 rest
, query
= rest
[:i
], rest
[i
+1:]
84 i
= string
.find(rest
, '/')
86 script
, rest
= rest
[:i
], rest
[i
:]
88 script
, rest
= rest
, ''
89 scriptname
= dir + '/' + script
90 scriptfile
= self
.translate_path(scriptname
)
91 if not os
.path
.exists(scriptfile
):
92 self
.send_error(404, "No such CGI script (%s)" % `scriptname`
)
94 if not os
.path
.isfile(scriptfile
):
95 self
.send_error(403, "CGI script is not a plain file (%s)" %
98 if not executable(scriptfile
):
99 self
.send_error(403, "CGI script is not executable (%s)" %
102 nobody
= nobody_uid()
103 self
.send_response(200, "Script output follows")
104 self
.wfile
.flush() # Always flush before forking
108 pid
, sts
= os
.waitpid(pid
, 0)
110 self
.log_error("CGI script exit status x%x" % sts
)
114 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
115 # XXX Much of the following could be prepared ahead of time!
117 env
['SERVER_SOFTWARE'] = self
.version_string()
118 env
['SERVER_NAME'] = self
.server
.server_name
119 env
['GATEWAY_INTERFACE'] = 'CGI/1.1'
120 env
['SERVER_PROTOCOL'] = self
.protocol_version
121 env
['SERVER_PORT'] = str(self
.server
.server_port
)
122 env
['REQUEST_METHOD'] = self
.command
123 uqrest
= urllib
.unquote(rest
)
124 env
['PATH_INFO'] = uqrest
125 env
['PATH_TRANSLATED'] = self
.translate_path(uqrest
)
126 env
['SCRIPT_NAME'] = scriptname
128 env
['QUERY_STRING'] = query
129 host
= self
.address_string()
130 if host
!= self
.client_address
[0]:
131 env
['REMOTE_HOST'] = host
132 env
['REMOTE_ADDR'] = self
.client_address
[0]
136 env
['CONTENT_TYPE'] = self
.headers
.type
137 length
= self
.headers
.getheader('content-length')
139 env
['CONTENT_LENGTH'] = length
141 for line
in self
.headers
.getallmatchingheaders('accept'):
142 if line
[:1] in string
.whitespace
:
143 accept
.append(string
.strip(line
))
145 accept
= accept
+ string
.split(line
[7:])
146 env
['HTTP_ACCEPT'] = string
.joinfields(accept
, ',')
147 ua
= self
.headers
.getheader('user-agent')
149 env
['HTTP_USER_AGENT'] = ua
150 # XXX Other HTTP_* headers
152 decoded_query
= regsub
.gsub('+', ' ', query
)
157 os
.dup2(self
.rfile
.fileno(), 0)
158 os
.dup2(self
.wfile
.fileno(), 1)
159 print scriptfile
, script
, decoded_query
160 os
.execve(scriptfile
,
161 [script
, decoded_query
],
164 self
.server
.handle_error(self
.request
, self
.client_address
)
171 """Internal routine to get nobody's uid"""
177 nobody
= pwd
.getpwnam('nobody')[2]
179 nobody
= 1 + max(map(lambda x
: x
[2], pwd
.getpwall()))
183 def executable(path
):
184 """Test for executable file."""
189 return st
[0] & 0111 != 0
192 def test(HandlerClass
= CGIHTTPRequestHandler
,
193 ServerClass
= BaseHTTPServer
.HTTPServer
):
195 if sys
.argv
[1:2] == ['-r']:
199 SimpleHTTPServer
.test(HandlerClass
, ServerClass
)
202 if __name__
== '__main__':