blob: e1b71abf37420665f5c36a2df9c1f1e160616aa0 [file] [log] [blame]
Georg Brandl24420152008-05-26 16:32:26 +00001"""HTTP server classes.
2
3Note: BaseHTTPRequestHandler doesn't implement any HTTP request; see
4SimpleHTTPRequestHandler for simple implementations of GET, HEAD and POST,
5and CGIHTTPRequestHandler for CGI scripts.
6
7It does, however, optionally implement HTTP/1.1 persistent connections,
8as of version 0.3.
9
10Notes on CGIHTTPRequestHandler
11------------------------------
12
13This class implements GET and POST requests to cgi-bin scripts.
14
15If the os.fork() function is not present (e.g. on Windows),
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +000016subprocess.Popen() is used as a fallback, with slightly altered semantics.
Georg Brandl24420152008-05-26 16:32:26 +000017
18In all cases, the implementation is intentionally naive -- all
19requests are executed synchronously.
20
21SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
22-- it may execute arbitrary Python code or external programs.
23
24Note that status code 200 is sent prior to execution of a CGI script, so
25scripts cannot send other status codes such as 302 (redirect).
26
27XXX To do:
28
29- log requests even later (to capture byte count)
30- log user-agent header and other interesting goodies
31- send error log to separate file
32"""
33
34
35# See also:
36#
37# HTTP Working Group T. Berners-Lee
38# INTERNET-DRAFT R. T. Fielding
39# <draft-ietf-http-v10-spec-00.txt> H. Frystyk Nielsen
40# Expires September 8, 1995 March 8, 1995
41#
42# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt
43#
44# and
45#
46# Network Working Group R. Fielding
47# Request for Comments: 2616 et al
48# Obsoletes: 2068 June 1999
49# Category: Standards Track
50#
51# URL: http://www.faqs.org/rfcs/rfc2616.html
52
53# Log files
54# ---------
55#
56# Here's a quote from the NCSA httpd docs about log file format.
57#
58# | The logfile format is as follows. Each line consists of:
59# |
60# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb
61# |
62# | host: Either the DNS name or the IP number of the remote client
63# | rfc931: Any information returned by identd for this person,
64# | - otherwise.
65# | authuser: If user sent a userid for authentication, the user name,
66# | - otherwise.
67# | DD: Day
68# | Mon: Month (calendar name)
69# | YYYY: Year
70# | hh: hour (24-hour format, the machine's timezone)
71# | mm: minutes
72# | ss: seconds
73# | request: The first line of the HTTP request as sent by the client.
74# | ddd: the status code returned by the server, - if not available.
75# | bbbb: the total number of bytes sent,
76# | *not including the HTTP/1.0 header*, - if not available
77# |
78# | You can determine the name of the file accessed through request.
79#
80# (Actually, the latter is only true if you know the server configuration
81# at the time the request was made!)
82
83__version__ = "0.6"
84
Berker Peksag366c5702015-02-13 20:48:15 +020085__all__ = [
86 "HTTPServer", "BaseHTTPRequestHandler",
87 "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler",
88]
Georg Brandl24420152008-05-26 16:32:26 +000089
Georg Brandl1f7fffb2010-10-15 15:57:45 +000090import html
Jeremy Hylton914ab452009-03-27 17:16:06 +000091import http.client
92import io
93import mimetypes
94import os
95import posixpath
96import select
97import shutil
98import socket # For gethostbyaddr()
99import socketserver
100import sys
101import time
102import urllib.parse
Senthil Kumaran42713722010-10-03 17:55:45 +0000103import copy
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800104import argparse
105
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200106from http import HTTPStatus
107
Georg Brandl24420152008-05-26 16:32:26 +0000108
109# Default error message template
110DEFAULT_ERROR_MESSAGE = """\
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800111<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
112 "http://www.w3.org/TR/html4/strict.dtd">
Ezio Melottica897e92011-11-02 19:33:29 +0200113<html>
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800114 <head>
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800115 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800116 <title>Error response</title>
117 </head>
118 <body>
119 <h1>Error response</h1>
120 <p>Error code: %(code)d</p>
121 <p>Message: %(message)s.</p>
122 <p>Error code explanation: %(code)s - %(explain)s.</p>
123 </body>
124</html>
Georg Brandl24420152008-05-26 16:32:26 +0000125"""
126
127DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8"
128
129def _quote_html(html):
130 return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
131
132class HTTPServer(socketserver.TCPServer):
133
134 allow_reuse_address = 1 # Seems to make sense in testing environment
135
136 def server_bind(self):
137 """Override server_bind to store the server name."""
138 socketserver.TCPServer.server_bind(self)
139 host, port = self.socket.getsockname()[:2]
140 self.server_name = socket.getfqdn(host)
141 self.server_port = port
142
143
144class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
145
146 """HTTP request handler base class.
147
148 The following explanation of HTTP serves to guide you through the
149 code as well as to expose any misunderstandings I may have about
150 HTTP (so you don't need to read the code to figure out I'm wrong
151 :-).
152
153 HTTP (HyperText Transfer Protocol) is an extensible protocol on
154 top of a reliable stream transport (e.g. TCP/IP). The protocol
155 recognizes three parts to a request:
156
157 1. One line identifying the request type and path
158 2. An optional set of RFC-822-style headers
159 3. An optional data part
160
161 The headers and data are separated by a blank line.
162
163 The first line of the request has the form
164
165 <command> <path> <version>
166
167 where <command> is a (case-sensitive) keyword such as GET or POST,
168 <path> is a string containing path information for the request,
169 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
170 <path> is encoded using the URL encoding scheme (using %xx to signify
171 the ASCII character with hex code xx).
172
173 The specification specifies that lines are separated by CRLF but
174 for compatibility with the widest range of clients recommends
175 servers also handle LF. Similarly, whitespace in the request line
176 is treated sensibly (allowing multiple spaces between components
177 and allowing trailing whitespace).
178
179 Similarly, for output, lines ought to be separated by CRLF pairs
180 but most clients grok LF characters just fine.
181
182 If the first line of the request has the form
183
184 <command> <path>
185
186 (i.e. <version> is left out) then this is assumed to be an HTTP
187 0.9 request; this form has no optional headers and data part and
188 the reply consists of just the data.
189
190 The reply form of the HTTP 1.x protocol again has three parts:
191
192 1. One line giving the response code
193 2. An optional set of RFC-822-style headers
194 3. The data
195
196 Again, the headers and data are separated by a blank line.
197
198 The response code line has the form
199
200 <version> <responsecode> <responsestring>
201
202 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
203 <responsecode> is a 3-digit response code indicating success or
204 failure of the request, and <responsestring> is an optional
205 human-readable string explaining what the response code means.
206
207 This server parses the request and the headers, and then calls a
208 function specific to the request type (<command>). Specifically,
209 a request SPAM will be handled by a method do_SPAM(). If no
210 such method exists the server sends an error response to the
211 client. If it exists, it is called with no arguments:
212
213 do_SPAM()
214
215 Note that the request name is case sensitive (i.e. SPAM and spam
216 are different requests).
217
218 The various request details are stored in instance variables:
219
220 - client_address is the client IP address in the form (host,
221 port);
222
223 - command, path and version are the broken-down request line;
224
Barry Warsaw820c1202008-06-12 04:06:45 +0000225 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000226 class) containing the header information;
227
228 - rfile is a file object open for reading positioned at the
229 start of the optional input data part;
230
231 - wfile is a file object open for writing.
232
233 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
234
235 The first thing to be written must be the response line. Then
236 follow 0 or more header lines, then a blank line, and then the
237 actual data (if any). The meaning of the header lines depends on
238 the command executed by the server; in most cases, when data is
239 returned, there should be at least one header line of the form
240
241 Content-type: <type>/<subtype>
242
243 where <type> and <subtype> should be registered MIME types,
244 e.g. "text/html" or "text/plain".
245
246 """
247
248 # The Python system version, truncated to its first component.
249 sys_version = "Python/" + sys.version.split()[0]
250
251 # The server software version. You may want to override this.
252 # The format is multiple whitespace-separated strings,
253 # where each string is of the form name[/version].
254 server_version = "BaseHTTP/" + __version__
255
256 error_message_format = DEFAULT_ERROR_MESSAGE
257 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
258
259 # The default request version. This only affects responses up until
260 # the point where the request line is parsed, so it mainly decides what
261 # the client gets back when sending a malformed request line.
262 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
263 default_request_version = "HTTP/0.9"
264
265 def parse_request(self):
266 """Parse a request (internal).
267
268 The request should be stored in self.raw_requestline; the results
269 are in self.command, self.path, self.request_version and
270 self.headers.
271
272 Return True for success, False for failure; on failure, an
273 error is sent back.
274
275 """
276 self.command = None # set in case of error on the first line
277 self.request_version = version = self.default_request_version
Benjamin Peterson70e28472015-02-17 21:11:10 -0500278 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000279 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800280 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000281 self.requestline = requestline
282 words = requestline.split()
283 if len(words) == 3:
Senthil Kumaran30755492011-12-23 17:03:41 +0800284 command, path, version = words
Georg Brandl24420152008-05-26 16:32:26 +0000285 if version[:5] != 'HTTP/':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200286 self.send_error(
287 HTTPStatus.BAD_REQUEST,
288 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000289 return False
290 try:
291 base_version_number = version.split('/', 1)[1]
292 version_number = base_version_number.split(".")
293 # RFC 2145 section 3.1 says there can be only one "." and
294 # - major and minor numbers MUST be treated as
295 # separate integers;
296 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
297 # turn is lower than HTTP/12.3;
298 # - Leading zeros MUST be ignored by recipients.
299 if len(version_number) != 2:
300 raise ValueError
301 version_number = int(version_number[0]), int(version_number[1])
302 except (ValueError, IndexError):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200303 self.send_error(
304 HTTPStatus.BAD_REQUEST,
305 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000306 return False
307 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
Benjamin Peterson70e28472015-02-17 21:11:10 -0500308 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000309 if version_number >= (2, 0):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200310 self.send_error(
311 HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
312 "Invalid HTTP Version (%s)" % base_version_number)
Georg Brandl24420152008-05-26 16:32:26 +0000313 return False
314 elif len(words) == 2:
Senthil Kumaran30755492011-12-23 17:03:41 +0800315 command, path = words
Benjamin Peterson70e28472015-02-17 21:11:10 -0500316 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000317 if command != 'GET':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200318 self.send_error(
319 HTTPStatus.BAD_REQUEST,
320 "Bad HTTP/0.9 request type (%r)" % command)
Georg Brandl24420152008-05-26 16:32:26 +0000321 return False
322 elif not words:
323 return False
324 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200325 self.send_error(
326 HTTPStatus.BAD_REQUEST,
327 "Bad request syntax (%r)" % requestline)
Georg Brandl24420152008-05-26 16:32:26 +0000328 return False
329 self.command, self.path, self.request_version = command, path, version
330
331 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000332 try:
333 self.headers = http.client.parse_headers(self.rfile,
334 _class=self.MessageClass)
335 except http.client.LineTooLong:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200336 self.send_error(
337 HTTPStatus.BAD_REQUEST,
338 "Line too long")
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000339 return False
Georg Brandl24420152008-05-26 16:32:26 +0000340
341 conntype = self.headers.get('Connection', "")
342 if conntype.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500343 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000344 elif (conntype.lower() == 'keep-alive' and
345 self.protocol_version >= "HTTP/1.1"):
Benjamin Peterson70e28472015-02-17 21:11:10 -0500346 self.close_connection = False
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000347 # Examine the headers and look for an Expect directive
348 expect = self.headers.get('Expect', "")
349 if (expect.lower() == "100-continue" and
350 self.protocol_version >= "HTTP/1.1" and
351 self.request_version >= "HTTP/1.1"):
352 if not self.handle_expect_100():
353 return False
354 return True
355
356 def handle_expect_100(self):
357 """Decide what to do with an "Expect: 100-continue" header.
358
359 If the client is expecting a 100 Continue response, we must
360 respond with either a 100 Continue or a final response before
361 waiting for the request body. The default is to always respond
362 with a 100 Continue. You can behave differently (for example,
363 reject unauthorized requests) by overriding this method.
364
365 This method should either return True (possibly after sending
366 a 100 Continue response) or send an error response and return
367 False.
368
369 """
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200370 self.send_response_only(HTTPStatus.CONTINUE)
Benjamin Peterson04424232014-01-18 21:50:18 -0500371 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000372 return True
373
374 def handle_one_request(self):
375 """Handle a single HTTP request.
376
377 You normally don't need to override this method; see the class
378 __doc__ string for information on how to handle specific HTTP
379 commands such as GET and POST.
380
381 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000382 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000383 self.raw_requestline = self.rfile.readline(65537)
384 if len(self.raw_requestline) > 65536:
385 self.requestline = ''
386 self.request_version = ''
387 self.command = ''
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200388 self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
Antoine Pitrouc4924372010-12-16 16:48:36 +0000389 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000390 if not self.raw_requestline:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500391 self.close_connection = True
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000392 return
393 if not self.parse_request():
394 # An error code has been sent, just exit
395 return
396 mname = 'do_' + self.command
397 if not hasattr(self, mname):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200398 self.send_error(
399 HTTPStatus.NOT_IMPLEMENTED,
400 "Unsupported method (%r)" % self.command)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000401 return
402 method = getattr(self, mname)
403 method()
404 self.wfile.flush() #actually send the response if not already done.
405 except socket.timeout as e:
406 #a read or a write timed out. Discard this connection
407 self.log_error("Request timed out: %r", e)
Benjamin Peterson70e28472015-02-17 21:11:10 -0500408 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000409 return
Georg Brandl24420152008-05-26 16:32:26 +0000410
411 def handle(self):
412 """Handle multiple requests if necessary."""
Benjamin Peterson70e28472015-02-17 21:11:10 -0500413 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000414
415 self.handle_one_request()
416 while not self.close_connection:
417 self.handle_one_request()
418
Senthil Kumaran26886442013-03-15 07:53:21 -0700419 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000420 """Send and log an error reply.
421
Senthil Kumaran26886442013-03-15 07:53:21 -0700422 Arguments are
423 * code: an HTTP error code
424 3 digits
425 * message: a simple optional 1 line reason phrase.
426 *( HTAB / SP / VCHAR / %x80-FF )
427 defaults to short entry matching the response code
428 * explain: a detailed message defaults to the long entry
429 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000430
431 This sends an error response (so it must be called before any
432 output has been generated), logs the error, and finally sends
433 a piece of HTML explaining the error to the user.
434
435 """
436
437 try:
438 shortmsg, longmsg = self.responses[code]
439 except KeyError:
440 shortmsg, longmsg = '???', '???'
441 if message is None:
442 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700443 if explain is None:
444 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000445 self.log_error("code %d, message %s", code, message)
446 # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
447 content = (self.error_message_format %
Senthil Kumaran26886442013-03-15 07:53:21 -0700448 {'code': code, 'message': _quote_html(message), 'explain': _quote_html(explain)})
Senthil Kumaran52d27202012-10-10 23:16:21 -0700449 body = content.encode('UTF-8', 'replace')
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800450 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000451 self.send_header("Content-Type", self.error_content_type)
452 self.send_header('Connection', 'close')
Senthil Kumaran52d27202012-10-10 23:16:21 -0700453 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000454 self.end_headers()
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200455
456 if (self.command != 'HEAD' and
457 code >= 200 and
458 code not in (
459 HTTPStatus.NO_CONTENT, HTTPStatus.NOT_MODIFIED)):
Senthil Kumaran52d27202012-10-10 23:16:21 -0700460 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000461
462 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800463 """Add the response header to the headers buffer and log the
464 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000465
466 Also send two standard headers with the server software
467 version and the current date.
468
469 """
470 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000471 self.send_response_only(code, message)
472 self.send_header('Server', self.version_string())
473 self.send_header('Date', self.date_time_string())
474
475 def send_response_only(self, code, message=None):
476 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000477 if message is None:
478 if code in self.responses:
479 message = self.responses[code][0]
480 else:
481 message = ''
482 if self.request_version != 'HTTP/0.9':
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800483 if not hasattr(self, '_headers_buffer'):
484 self._headers_buffer = []
485 self._headers_buffer.append(("%s %d %s\r\n" %
486 (self.protocol_version, code, message)).encode(
487 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000488
489 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800490 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000491 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000492 if not hasattr(self, '_headers_buffer'):
493 self._headers_buffer = []
494 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000495 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000496
497 if keyword.lower() == 'connection':
498 if value.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500499 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000500 elif value.lower() == 'keep-alive':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500501 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000502
503 def end_headers(self):
504 """Send the blank line ending the MIME headers."""
505 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000506 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800507 self.flush_headers()
508
509 def flush_headers(self):
510 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000511 self.wfile.write(b"".join(self._headers_buffer))
512 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000513
514 def log_request(self, code='-', size='-'):
515 """Log an accepted request.
516
517 This is called by send_response().
518
519 """
Serhiy Storchakac0a23e62015-03-07 11:51:37 +0200520 if isinstance(code, HTTPStatus):
521 code = code.value
Georg Brandl24420152008-05-26 16:32:26 +0000522 self.log_message('"%s" %s %s',
523 self.requestline, str(code), str(size))
524
525 def log_error(self, format, *args):
526 """Log an error.
527
528 This is called when a request cannot be fulfilled. By
529 default it passes the message on to log_message().
530
531 Arguments are the same as for log_message().
532
533 XXX This should go to the separate error log.
534
535 """
536
537 self.log_message(format, *args)
538
539 def log_message(self, format, *args):
540 """Log an arbitrary message.
541
542 This is used by all other logging functions. Override
543 it if you have specific logging wishes.
544
545 The first argument, FORMAT, is a format string for the
546 message to be logged. If the format string contains
547 any % escapes requiring parameters, they should be
548 specified as subsequent arguments (it's just like
549 printf!).
550
Senthil Kumarandb727b42012-04-29 13:41:03 +0800551 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000552 every message.
553
554 """
555
556 sys.stderr.write("%s - - [%s] %s\n" %
557 (self.address_string(),
558 self.log_date_time_string(),
559 format%args))
560
561 def version_string(self):
562 """Return the server software version string."""
563 return self.server_version + ' ' + self.sys_version
564
565 def date_time_string(self, timestamp=None):
566 """Return the current date and time formatted for a message header."""
567 if timestamp is None:
568 timestamp = time.time()
569 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
570 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
571 self.weekdayname[wd],
572 day, self.monthname[month], year,
573 hh, mm, ss)
574 return s
575
576 def log_date_time_string(self):
577 """Return the current time formatted for logging."""
578 now = time.time()
579 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
580 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
581 day, self.monthname[month], year, hh, mm, ss)
582 return s
583
584 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
585
586 monthname = [None,
587 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
588 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
589
590 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800591 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000592
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800593 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000594
595 # Essentially static class variables
596
597 # The version of the HTTP protocol we support.
598 # Set this to HTTP/1.1 to enable automatic keepalive
599 protocol_version = "HTTP/1.0"
600
Barry Warsaw820c1202008-06-12 04:06:45 +0000601 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000602 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000603
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200604 # hack to maintain backwards compatibility
Georg Brandl24420152008-05-26 16:32:26 +0000605 responses = {
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200606 v: (v.phrase, v.description)
607 for v in HTTPStatus.__members__.values()
608 }
Georg Brandl24420152008-05-26 16:32:26 +0000609
610
611class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
612
613 """Simple HTTP request handler with GET and HEAD commands.
614
615 This serves files from the current directory and any of its
616 subdirectories. The MIME type for files is determined by
617 calling the .guess_type() method.
618
619 The GET and HEAD requests are identical except that the HEAD
620 request omits the actual contents of the file.
621
622 """
623
624 server_version = "SimpleHTTP/" + __version__
625
626 def do_GET(self):
627 """Serve a GET request."""
628 f = self.send_head()
629 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200630 try:
631 self.copyfile(f, self.wfile)
632 finally:
633 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000634
635 def do_HEAD(self):
636 """Serve a HEAD request."""
637 f = self.send_head()
638 if f:
639 f.close()
640
641 def send_head(self):
642 """Common code for GET and HEAD commands.
643
644 This sends the response code and MIME headers.
645
646 Return value is either a file object (which has to be copied
647 to the outputfile by the caller unless the command was HEAD,
648 and must be closed by the caller under all circumstances), or
649 None, in which case the caller has nothing further to do.
650
651 """
652 path = self.translate_path(self.path)
653 f = None
654 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600655 parts = urllib.parse.urlsplit(self.path)
656 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000657 # redirect browser - doing basically what apache does
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200658 self.send_response(HTTPStatus.MOVED_PERMANENTLY)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600659 new_parts = (parts[0], parts[1], parts[2] + '/',
660 parts[3], parts[4])
661 new_url = urllib.parse.urlunsplit(new_parts)
662 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000663 self.end_headers()
664 return None
665 for index in "index.html", "index.htm":
666 index = os.path.join(path, index)
667 if os.path.exists(index):
668 path = index
669 break
670 else:
671 return self.list_directory(path)
672 ctype = self.guess_type(path)
673 try:
674 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200675 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200676 self.send_error(HTTPStatus.NOT_FOUND, "File not found")
Georg Brandl24420152008-05-26 16:32:26 +0000677 return None
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200678 try:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200679 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200680 self.send_header("Content-type", ctype)
681 fs = os.fstat(f.fileno())
682 self.send_header("Content-Length", str(fs[6]))
683 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
684 self.end_headers()
685 return f
686 except:
687 f.close()
688 raise
Georg Brandl24420152008-05-26 16:32:26 +0000689
690 def list_directory(self, path):
691 """Helper to produce a directory listing (absent index.html).
692
693 Return value is either a file object, or None (indicating an
694 error). In either case, the headers are sent, making the
695 interface the same as for send_head().
696
697 """
698 try:
699 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200700 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200701 self.send_error(
702 HTTPStatus.NOT_FOUND,
703 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000704 return None
705 list.sort(key=lambda a: a.lower())
706 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300707 try:
708 displaypath = urllib.parse.unquote(self.path,
709 errors='surrogatepass')
710 except UnicodeDecodeError:
711 displaypath = urllib.parse.unquote(path)
712 displaypath = html.escape(displaypath)
Ezio Melottica897e92011-11-02 19:33:29 +0200713 enc = sys.getfilesystemencoding()
714 title = 'Directory listing for %s' % displaypath
715 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
716 '"http://www.w3.org/TR/html4/strict.dtd">')
717 r.append('<html>\n<head>')
718 r.append('<meta http-equiv="Content-Type" '
719 'content="text/html; charset=%s">' % enc)
720 r.append('<title>%s</title>\n</head>' % title)
721 r.append('<body>\n<h1>%s</h1>' % title)
722 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000723 for name in list:
724 fullname = os.path.join(path, name)
725 displayname = linkname = name
726 # Append / for directories or @ for symbolic links
727 if os.path.isdir(fullname):
728 displayname = name + "/"
729 linkname = name + "/"
730 if os.path.islink(fullname):
731 displayname = name + "@"
732 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200733 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300734 % (urllib.parse.quote(linkname,
735 errors='surrogatepass'),
736 html.escape(displayname)))
Ezio Melottica897e92011-11-02 19:33:29 +0200737 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300738 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000739 f = io.BytesIO()
740 f.write(encoded)
741 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200742 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000743 self.send_header("Content-type", "text/html; charset=%s" % enc)
744 self.send_header("Content-Length", str(len(encoded)))
745 self.end_headers()
746 return f
747
748 def translate_path(self, path):
749 """Translate a /-separated PATH to the local filename syntax.
750
751 Components that mean special things to the local file system
752 (e.g. drive or directory names) are ignored. (XXX They should
753 probably be diagnosed.)
754
755 """
756 # abandon query parameters
757 path = path.split('?',1)[0]
758 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700759 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700760 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300761 try:
762 path = urllib.parse.unquote(path, errors='surrogatepass')
763 except UnicodeDecodeError:
764 path = urllib.parse.unquote(path)
765 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000766 words = path.split('/')
767 words = filter(None, words)
768 path = os.getcwd()
769 for word in words:
770 drive, word = os.path.splitdrive(word)
771 head, word = os.path.split(word)
772 if word in (os.curdir, os.pardir): continue
773 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700774 if trailing_slash:
775 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000776 return path
777
778 def copyfile(self, source, outputfile):
779 """Copy all data between two file objects.
780
781 The SOURCE argument is a file object open for reading
782 (or anything with a read() method) and the DESTINATION
783 argument is a file object open for writing (or
784 anything with a write() method).
785
786 The only reason for overriding this would be to change
787 the block size or perhaps to replace newlines by CRLF
788 -- note however that this the default server uses this
789 to copy binary data as well.
790
791 """
792 shutil.copyfileobj(source, outputfile)
793
794 def guess_type(self, path):
795 """Guess the type of a file.
796
797 Argument is a PATH (a filename).
798
799 Return value is a string of the form type/subtype,
800 usable for a MIME Content-type header.
801
802 The default implementation looks the file's extension
803 up in the table self.extensions_map, using application/octet-stream
804 as a default; however it would be permissible (if
805 slow) to look inside the data to make a better guess.
806
807 """
808
809 base, ext = posixpath.splitext(path)
810 if ext in self.extensions_map:
811 return self.extensions_map[ext]
812 ext = ext.lower()
813 if ext in self.extensions_map:
814 return self.extensions_map[ext]
815 else:
816 return self.extensions_map['']
817
818 if not mimetypes.inited:
819 mimetypes.init() # try to read system mime.types
820 extensions_map = mimetypes.types_map.copy()
821 extensions_map.update({
822 '': 'application/octet-stream', # Default
823 '.py': 'text/plain',
824 '.c': 'text/plain',
825 '.h': 'text/plain',
826 })
827
828
829# Utilities for CGIHTTPRequestHandler
830
Senthil Kumarand70846b2012-04-12 02:34:32 +0800831def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000832 """
833 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000834 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000835
836 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800837 The utility of this function is limited to is_cgi method and helps
838 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000839
Martin Pantercb29e8c2015-10-03 05:55:46 +0000840 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000841
842 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800843
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000844 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000845 # Query component should not be involved.
846 path, _, query = path.partition('?')
847 path = urllib.parse.unquote(path)
848
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000849 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
850 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800851 path_parts = path.split('/')
852 head_parts = []
853 for part in path_parts[:-1]:
854 if part == '..':
855 head_parts.pop() # IndexError if more '..' than prior parts
856 elif part and part != '.':
857 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000858 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800859 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800860 if tail_part:
861 if tail_part == '..':
862 head_parts.pop()
863 tail_part = ''
864 elif tail_part == '.':
865 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000866 else:
867 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800868
Martin Pantercb29e8c2015-10-03 05:55:46 +0000869 if query:
870 tail_part = '?'.join((tail_part, query))
871
Senthil Kumarand70846b2012-04-12 02:34:32 +0800872 splitpath = ('/' + '/'.join(head_parts), tail_part)
873 collapsed_path = "/".join(splitpath)
874
875 return collapsed_path
876
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000877
878
Georg Brandl24420152008-05-26 16:32:26 +0000879nobody = None
880
881def nobody_uid():
882 """Internal routine to get nobody's uid"""
883 global nobody
884 if nobody:
885 return nobody
886 try:
887 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400888 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000889 return -1
890 try:
891 nobody = pwd.getpwnam('nobody')[2]
892 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000893 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000894 return nobody
895
896
897def executable(path):
898 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200899 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000900
901
902class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
903
904 """Complete HTTP server with GET, HEAD and POST commands.
905
906 GET and HEAD also support running CGI scripts.
907
908 The POST command is *only* implemented for CGI scripts.
909
910 """
911
912 # Determine platform specifics
913 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000914
915 # Make rfile unbuffered -- we need to read one line and then pass
916 # the rest to a subprocess, so we can't use buffered input.
917 rbufsize = 0
918
919 def do_POST(self):
920 """Serve a POST request.
921
922 This is only implemented for CGI scripts.
923
924 """
925
926 if self.is_cgi():
927 self.run_cgi()
928 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200929 self.send_error(
930 HTTPStatus.NOT_IMPLEMENTED,
931 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000932
933 def send_head(self):
934 """Version of send_head that support CGI scripts"""
935 if self.is_cgi():
936 return self.run_cgi()
937 else:
938 return SimpleHTTPRequestHandler.send_head(self)
939
940 def is_cgi(self):
941 """Test whether self.path corresponds to a CGI script.
942
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000943 Returns True and updates the cgi_info attribute to the tuple
944 (dir, rest) if self.path requires running a CGI script.
945 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000946
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000947 If any exception is raised, the caller should assume that
948 self.path was rejected as invalid and act accordingly.
949
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000950 The default implementation tests whether the normalized url
951 path begins with one of the strings in self.cgi_directories
952 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000953
954 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000955 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +0800956 dir_sep = collapsed_path.find('/', 1)
957 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800958 if head in self.cgi_directories:
959 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000960 return True
Georg Brandl24420152008-05-26 16:32:26 +0000961 return False
962
Senthil Kumarand70846b2012-04-12 02:34:32 +0800963
Georg Brandl24420152008-05-26 16:32:26 +0000964 cgi_directories = ['/cgi-bin', '/htbin']
965
966 def is_executable(self, path):
967 """Test whether argument path is an executable file."""
968 return executable(path)
969
970 def is_python(self, path):
971 """Test whether argument path is a Python script."""
972 head, tail = os.path.splitext(path)
973 return tail.lower() in (".py", ".pyw")
974
975 def run_cgi(self):
976 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +0000977 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -0700978 path = dir + '/' + rest
979 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000980 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -0700981 nextdir = path[:i]
982 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +0000983
984 scriptdir = self.translate_path(nextdir)
985 if os.path.isdir(scriptdir):
986 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -0700987 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000988 else:
989 break
990
991 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +0000992 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +0000993
994 # dissect the part after the directory name into a script name &
995 # a possible additional path, to be stored in PATH_INFO.
996 i = rest.find('/')
997 if i >= 0:
998 script, rest = rest[:i], rest[i:]
999 else:
1000 script, rest = rest, ''
1001
1002 scriptname = dir + '/' + script
1003 scriptfile = self.translate_path(scriptname)
1004 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001005 self.send_error(
1006 HTTPStatus.NOT_FOUND,
1007 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001008 return
1009 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001010 self.send_error(
1011 HTTPStatus.FORBIDDEN,
1012 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001013 return
1014 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001015 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001016 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001017 self.send_error(
1018 HTTPStatus.FORBIDDEN,
1019 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001020 return
1021
1022 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1023 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001024 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001025 env['SERVER_SOFTWARE'] = self.version_string()
1026 env['SERVER_NAME'] = self.server.server_name
1027 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1028 env['SERVER_PROTOCOL'] = self.protocol_version
1029 env['SERVER_PORT'] = str(self.server.server_port)
1030 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001031 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001032 env['PATH_INFO'] = uqrest
1033 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1034 env['SCRIPT_NAME'] = scriptname
1035 if query:
1036 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001037 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001038 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001039 if authorization:
1040 authorization = authorization.split()
1041 if len(authorization) == 2:
1042 import base64, binascii
1043 env['AUTH_TYPE'] = authorization[0]
1044 if authorization[0].lower() == "basic":
1045 try:
1046 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001047 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001048 decode('ascii')
1049 except (binascii.Error, UnicodeError):
1050 pass
1051 else:
1052 authorization = authorization.split(':')
1053 if len(authorization) == 2:
1054 env['REMOTE_USER'] = authorization[0]
1055 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001056 if self.headers.get('content-type') is None:
1057 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001058 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001059 env['CONTENT_TYPE'] = self.headers['content-type']
1060 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001061 if length:
1062 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001063 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001064 if referer:
1065 env['HTTP_REFERER'] = referer
1066 accept = []
1067 for line in self.headers.getallmatchingheaders('accept'):
1068 if line[:1] in "\t\n\r ":
1069 accept.append(line.strip())
1070 else:
1071 accept = accept + line[7:].split(',')
1072 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001073 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001074 if ua:
1075 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001076 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001077 cookie_str = ', '.join(co)
1078 if cookie_str:
1079 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001080 # XXX Other HTTP_* headers
1081 # Since we're setting the env in the parent, provide empty
1082 # values to override previously set values
1083 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1084 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1085 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001086
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001087 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001088 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001089
1090 decoded_query = query.replace('+', ' ')
1091
1092 if self.have_fork:
1093 # Unix -- fork as we should
1094 args = [script]
1095 if '=' not in decoded_query:
1096 args.append(decoded_query)
1097 nobody = nobody_uid()
1098 self.wfile.flush() # Always flush before forking
1099 pid = os.fork()
1100 if pid != 0:
1101 # Parent
1102 pid, sts = os.waitpid(pid, 0)
1103 # throw away additional data [see bug #427345]
1104 while select.select([self.rfile], [], [], 0)[0]:
1105 if not self.rfile.read(1):
1106 break
1107 if sts:
1108 self.log_error("CGI script exit status %#x", sts)
1109 return
1110 # Child
1111 try:
1112 try:
1113 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001114 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001115 pass
1116 os.dup2(self.rfile.fileno(), 0)
1117 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001118 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001119 except:
1120 self.server.handle_error(self.request, self.client_address)
1121 os._exit(127)
1122
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001123 else:
1124 # Non-Unix -- use subprocess
1125 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001126 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001127 if self.is_python(scriptfile):
1128 interp = sys.executable
1129 if interp.lower().endswith("w.exe"):
1130 # On Windows, use python.exe, not pythonw.exe
1131 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001132 cmdline = [interp, '-u'] + cmdline
1133 if '=' not in query:
1134 cmdline.append(query)
1135 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001136 try:
1137 nbytes = int(length)
1138 except (TypeError, ValueError):
1139 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001140 p = subprocess.Popen(cmdline,
1141 stdin=subprocess.PIPE,
1142 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001143 stderr=subprocess.PIPE,
1144 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001145 )
Georg Brandl24420152008-05-26 16:32:26 +00001146 if self.command.lower() == "post" and nbytes > 0:
1147 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001148 else:
1149 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001150 # throw away additional data [see bug #427345]
1151 while select.select([self.rfile._sock], [], [], 0)[0]:
1152 if not self.rfile._sock.recv(1):
1153 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001154 stdout, stderr = p.communicate(data)
1155 self.wfile.write(stdout)
1156 if stderr:
1157 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001158 p.stderr.close()
1159 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001160 status = p.returncode
1161 if status:
1162 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001163 else:
1164 self.log_message("CGI script exited OK")
1165
1166
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001167def test(HandlerClass=BaseHTTPRequestHandler,
1168 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001169 """Test the HTTP request handler class.
1170
Robert Collins9644f242015-08-17 12:18:35 +12001171 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001172
1173 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001174 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001175
1176 HandlerClass.protocol_version = protocol
1177 httpd = ServerClass(server_address, HandlerClass)
1178
1179 sa = httpd.socket.getsockname()
1180 print("Serving HTTP on", sa[0], "port", sa[1], "...")
Alexandre Vassalottib5292a22009-04-03 07:16:55 +00001181 try:
1182 httpd.serve_forever()
1183 except KeyboardInterrupt:
1184 print("\nKeyboard interrupt received, exiting.")
1185 httpd.server_close()
1186 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001187
1188if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001189 parser = argparse.ArgumentParser()
1190 parser.add_argument('--cgi', action='store_true',
1191 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001192 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1193 help='Specify alternate bind address '
1194 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001195 parser.add_argument('port', action='store',
1196 default=8000, type=int,
1197 nargs='?',
1198 help='Specify alternate port [default: 8000]')
1199 args = parser.parse_args()
1200 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001201 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001202 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001203 handler_class = SimpleHTTPRequestHandler
1204 test(HandlerClass=handler_class, port=args.port, bind=args.bind)