blob: 00620d1f853fafa764631f22d000a59b3eb06ba7 [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
Martin Panteracc03192016-04-03 00:45:46 +0000340 except http.client.HTTPException as err:
341 self.send_error(
342 HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
343 "Too many headers",
344 str(err)
345 )
346 return False
Georg Brandl24420152008-05-26 16:32:26 +0000347
348 conntype = self.headers.get('Connection', "")
349 if conntype.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500350 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000351 elif (conntype.lower() == 'keep-alive' and
352 self.protocol_version >= "HTTP/1.1"):
Benjamin Peterson70e28472015-02-17 21:11:10 -0500353 self.close_connection = False
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000354 # Examine the headers and look for an Expect directive
355 expect = self.headers.get('Expect', "")
356 if (expect.lower() == "100-continue" and
357 self.protocol_version >= "HTTP/1.1" and
358 self.request_version >= "HTTP/1.1"):
359 if not self.handle_expect_100():
360 return False
361 return True
362
363 def handle_expect_100(self):
364 """Decide what to do with an "Expect: 100-continue" header.
365
366 If the client is expecting a 100 Continue response, we must
367 respond with either a 100 Continue or a final response before
368 waiting for the request body. The default is to always respond
369 with a 100 Continue. You can behave differently (for example,
370 reject unauthorized requests) by overriding this method.
371
372 This method should either return True (possibly after sending
373 a 100 Continue response) or send an error response and return
374 False.
375
376 """
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200377 self.send_response_only(HTTPStatus.CONTINUE)
Benjamin Peterson04424232014-01-18 21:50:18 -0500378 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000379 return True
380
381 def handle_one_request(self):
382 """Handle a single HTTP request.
383
384 You normally don't need to override this method; see the class
385 __doc__ string for information on how to handle specific HTTP
386 commands such as GET and POST.
387
388 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000389 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000390 self.raw_requestline = self.rfile.readline(65537)
391 if len(self.raw_requestline) > 65536:
392 self.requestline = ''
393 self.request_version = ''
394 self.command = ''
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200395 self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
Antoine Pitrouc4924372010-12-16 16:48:36 +0000396 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000397 if not self.raw_requestline:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500398 self.close_connection = True
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000399 return
400 if not self.parse_request():
401 # An error code has been sent, just exit
402 return
403 mname = 'do_' + self.command
404 if not hasattr(self, mname):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200405 self.send_error(
406 HTTPStatus.NOT_IMPLEMENTED,
407 "Unsupported method (%r)" % self.command)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000408 return
409 method = getattr(self, mname)
410 method()
411 self.wfile.flush() #actually send the response if not already done.
412 except socket.timeout as e:
413 #a read or a write timed out. Discard this connection
414 self.log_error("Request timed out: %r", e)
Benjamin Peterson70e28472015-02-17 21:11:10 -0500415 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000416 return
Georg Brandl24420152008-05-26 16:32:26 +0000417
418 def handle(self):
419 """Handle multiple requests if necessary."""
Benjamin Peterson70e28472015-02-17 21:11:10 -0500420 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000421
422 self.handle_one_request()
423 while not self.close_connection:
424 self.handle_one_request()
425
Senthil Kumaran26886442013-03-15 07:53:21 -0700426 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000427 """Send and log an error reply.
428
Senthil Kumaran26886442013-03-15 07:53:21 -0700429 Arguments are
430 * code: an HTTP error code
431 3 digits
432 * message: a simple optional 1 line reason phrase.
433 *( HTAB / SP / VCHAR / %x80-FF )
434 defaults to short entry matching the response code
435 * explain: a detailed message defaults to the long entry
436 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000437
438 This sends an error response (so it must be called before any
439 output has been generated), logs the error, and finally sends
440 a piece of HTML explaining the error to the user.
441
442 """
443
444 try:
445 shortmsg, longmsg = self.responses[code]
446 except KeyError:
447 shortmsg, longmsg = '???', '???'
448 if message is None:
449 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700450 if explain is None:
451 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000452 self.log_error("code %d, message %s", code, message)
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800453 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000454 self.send_header('Connection', 'close')
Martin Pantere42e1292016-06-08 08:29:13 +0000455
456 # Message body is omitted for cases described in:
457 # - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
458 # - RFC7231: 6.3.6. 205(Reset Content)
459 body = None
460 if (code >= 200 and
461 code not in (HTTPStatus.NO_CONTENT,
462 HTTPStatus.RESET_CONTENT,
463 HTTPStatus.NOT_MODIFIED)):
464 # HTML encode to prevent Cross Site Scripting attacks
465 # (see bug #1100201)
466 content = (self.error_message_format % {
467 'code': code,
468 'message': _quote_html(message),
469 'explain': _quote_html(explain)
470 })
471 body = content.encode('UTF-8', 'replace')
472 self.send_header("Content-Type", self.error_content_type)
473 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000474 self.end_headers()
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200475
Martin Pantere42e1292016-06-08 08:29:13 +0000476 if self.command != 'HEAD' and body:
Senthil Kumaran52d27202012-10-10 23:16:21 -0700477 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000478
479 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800480 """Add the response header to the headers buffer and log the
481 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000482
483 Also send two standard headers with the server software
484 version and the current date.
485
486 """
487 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000488 self.send_response_only(code, message)
489 self.send_header('Server', self.version_string())
490 self.send_header('Date', self.date_time_string())
491
492 def send_response_only(self, code, message=None):
493 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000494 if message is None:
495 if code in self.responses:
496 message = self.responses[code][0]
497 else:
498 message = ''
499 if self.request_version != 'HTTP/0.9':
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800500 if not hasattr(self, '_headers_buffer'):
501 self._headers_buffer = []
502 self._headers_buffer.append(("%s %d %s\r\n" %
503 (self.protocol_version, code, message)).encode(
504 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000505
506 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800507 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000508 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000509 if not hasattr(self, '_headers_buffer'):
510 self._headers_buffer = []
511 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000512 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000513
514 if keyword.lower() == 'connection':
515 if value.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500516 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000517 elif value.lower() == 'keep-alive':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500518 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000519
520 def end_headers(self):
521 """Send the blank line ending the MIME headers."""
522 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000523 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800524 self.flush_headers()
525
526 def flush_headers(self):
527 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000528 self.wfile.write(b"".join(self._headers_buffer))
529 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000530
531 def log_request(self, code='-', size='-'):
532 """Log an accepted request.
533
534 This is called by send_response().
535
536 """
Serhiy Storchakac0a23e62015-03-07 11:51:37 +0200537 if isinstance(code, HTTPStatus):
538 code = code.value
Georg Brandl24420152008-05-26 16:32:26 +0000539 self.log_message('"%s" %s %s',
540 self.requestline, str(code), str(size))
541
542 def log_error(self, format, *args):
543 """Log an error.
544
545 This is called when a request cannot be fulfilled. By
546 default it passes the message on to log_message().
547
548 Arguments are the same as for log_message().
549
550 XXX This should go to the separate error log.
551
552 """
553
554 self.log_message(format, *args)
555
556 def log_message(self, format, *args):
557 """Log an arbitrary message.
558
559 This is used by all other logging functions. Override
560 it if you have specific logging wishes.
561
562 The first argument, FORMAT, is a format string for the
563 message to be logged. If the format string contains
564 any % escapes requiring parameters, they should be
565 specified as subsequent arguments (it's just like
566 printf!).
567
Senthil Kumarandb727b42012-04-29 13:41:03 +0800568 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000569 every message.
570
571 """
572
573 sys.stderr.write("%s - - [%s] %s\n" %
574 (self.address_string(),
575 self.log_date_time_string(),
576 format%args))
577
578 def version_string(self):
579 """Return the server software version string."""
580 return self.server_version + ' ' + self.sys_version
581
582 def date_time_string(self, timestamp=None):
583 """Return the current date and time formatted for a message header."""
584 if timestamp is None:
585 timestamp = time.time()
586 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
587 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
588 self.weekdayname[wd],
589 day, self.monthname[month], year,
590 hh, mm, ss)
591 return s
592
593 def log_date_time_string(self):
594 """Return the current time formatted for logging."""
595 now = time.time()
596 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
597 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
598 day, self.monthname[month], year, hh, mm, ss)
599 return s
600
601 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
602
603 monthname = [None,
604 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
605 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
606
607 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800608 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000609
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800610 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000611
612 # Essentially static class variables
613
614 # The version of the HTTP protocol we support.
615 # Set this to HTTP/1.1 to enable automatic keepalive
616 protocol_version = "HTTP/1.0"
617
Barry Warsaw820c1202008-06-12 04:06:45 +0000618 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000619 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000620
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200621 # hack to maintain backwards compatibility
Georg Brandl24420152008-05-26 16:32:26 +0000622 responses = {
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200623 v: (v.phrase, v.description)
624 for v in HTTPStatus.__members__.values()
625 }
Georg Brandl24420152008-05-26 16:32:26 +0000626
627
628class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
629
630 """Simple HTTP request handler with GET and HEAD commands.
631
632 This serves files from the current directory and any of its
633 subdirectories. The MIME type for files is determined by
634 calling the .guess_type() method.
635
636 The GET and HEAD requests are identical except that the HEAD
637 request omits the actual contents of the file.
638
639 """
640
641 server_version = "SimpleHTTP/" + __version__
642
643 def do_GET(self):
644 """Serve a GET request."""
645 f = self.send_head()
646 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200647 try:
648 self.copyfile(f, self.wfile)
649 finally:
650 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000651
652 def do_HEAD(self):
653 """Serve a HEAD request."""
654 f = self.send_head()
655 if f:
656 f.close()
657
658 def send_head(self):
659 """Common code for GET and HEAD commands.
660
661 This sends the response code and MIME headers.
662
663 Return value is either a file object (which has to be copied
664 to the outputfile by the caller unless the command was HEAD,
665 and must be closed by the caller under all circumstances), or
666 None, in which case the caller has nothing further to do.
667
668 """
669 path = self.translate_path(self.path)
670 f = None
671 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600672 parts = urllib.parse.urlsplit(self.path)
673 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000674 # redirect browser - doing basically what apache does
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200675 self.send_response(HTTPStatus.MOVED_PERMANENTLY)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600676 new_parts = (parts[0], parts[1], parts[2] + '/',
677 parts[3], parts[4])
678 new_url = urllib.parse.urlunsplit(new_parts)
679 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000680 self.end_headers()
681 return None
682 for index in "index.html", "index.htm":
683 index = os.path.join(path, index)
684 if os.path.exists(index):
685 path = index
686 break
687 else:
688 return self.list_directory(path)
689 ctype = self.guess_type(path)
690 try:
691 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200692 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200693 self.send_error(HTTPStatus.NOT_FOUND, "File not found")
Georg Brandl24420152008-05-26 16:32:26 +0000694 return None
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200695 try:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200696 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200697 self.send_header("Content-type", ctype)
698 fs = os.fstat(f.fileno())
699 self.send_header("Content-Length", str(fs[6]))
700 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
701 self.end_headers()
702 return f
703 except:
704 f.close()
705 raise
Georg Brandl24420152008-05-26 16:32:26 +0000706
707 def list_directory(self, path):
708 """Helper to produce a directory listing (absent index.html).
709
710 Return value is either a file object, or None (indicating an
711 error). In either case, the headers are sent, making the
712 interface the same as for send_head().
713
714 """
715 try:
716 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200717 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200718 self.send_error(
719 HTTPStatus.NOT_FOUND,
720 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000721 return None
722 list.sort(key=lambda a: a.lower())
723 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300724 try:
725 displaypath = urllib.parse.unquote(self.path,
726 errors='surrogatepass')
727 except UnicodeDecodeError:
728 displaypath = urllib.parse.unquote(path)
729 displaypath = html.escape(displaypath)
Ezio Melottica897e92011-11-02 19:33:29 +0200730 enc = sys.getfilesystemencoding()
731 title = 'Directory listing for %s' % displaypath
732 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
733 '"http://www.w3.org/TR/html4/strict.dtd">')
734 r.append('<html>\n<head>')
735 r.append('<meta http-equiv="Content-Type" '
736 'content="text/html; charset=%s">' % enc)
737 r.append('<title>%s</title>\n</head>' % title)
738 r.append('<body>\n<h1>%s</h1>' % title)
739 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000740 for name in list:
741 fullname = os.path.join(path, name)
742 displayname = linkname = name
743 # Append / for directories or @ for symbolic links
744 if os.path.isdir(fullname):
745 displayname = name + "/"
746 linkname = name + "/"
747 if os.path.islink(fullname):
748 displayname = name + "@"
749 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200750 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300751 % (urllib.parse.quote(linkname,
752 errors='surrogatepass'),
753 html.escape(displayname)))
Ezio Melottica897e92011-11-02 19:33:29 +0200754 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300755 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000756 f = io.BytesIO()
757 f.write(encoded)
758 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200759 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000760 self.send_header("Content-type", "text/html; charset=%s" % enc)
761 self.send_header("Content-Length", str(len(encoded)))
762 self.end_headers()
763 return f
764
765 def translate_path(self, path):
766 """Translate a /-separated PATH to the local filename syntax.
767
768 Components that mean special things to the local file system
769 (e.g. drive or directory names) are ignored. (XXX They should
770 probably be diagnosed.)
771
772 """
773 # abandon query parameters
774 path = path.split('?',1)[0]
775 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700776 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700777 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300778 try:
779 path = urllib.parse.unquote(path, errors='surrogatepass')
780 except UnicodeDecodeError:
781 path = urllib.parse.unquote(path)
782 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000783 words = path.split('/')
784 words = filter(None, words)
785 path = os.getcwd()
786 for word in words:
Martin Panterd274b3f2016-04-18 03:45:18 +0000787 if os.path.dirname(word) or word in (os.curdir, os.pardir):
788 # Ignore components that are not a simple file/directory name
789 continue
Georg Brandl24420152008-05-26 16:32:26 +0000790 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700791 if trailing_slash:
792 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000793 return path
794
795 def copyfile(self, source, outputfile):
796 """Copy all data between two file objects.
797
798 The SOURCE argument is a file object open for reading
799 (or anything with a read() method) and the DESTINATION
800 argument is a file object open for writing (or
801 anything with a write() method).
802
803 The only reason for overriding this would be to change
804 the block size or perhaps to replace newlines by CRLF
805 -- note however that this the default server uses this
806 to copy binary data as well.
807
808 """
809 shutil.copyfileobj(source, outputfile)
810
811 def guess_type(self, path):
812 """Guess the type of a file.
813
814 Argument is a PATH (a filename).
815
816 Return value is a string of the form type/subtype,
817 usable for a MIME Content-type header.
818
819 The default implementation looks the file's extension
820 up in the table self.extensions_map, using application/octet-stream
821 as a default; however it would be permissible (if
822 slow) to look inside the data to make a better guess.
823
824 """
825
826 base, ext = posixpath.splitext(path)
827 if ext in self.extensions_map:
828 return self.extensions_map[ext]
829 ext = ext.lower()
830 if ext in self.extensions_map:
831 return self.extensions_map[ext]
832 else:
833 return self.extensions_map['']
834
835 if not mimetypes.inited:
836 mimetypes.init() # try to read system mime.types
837 extensions_map = mimetypes.types_map.copy()
838 extensions_map.update({
839 '': 'application/octet-stream', # Default
840 '.py': 'text/plain',
841 '.c': 'text/plain',
842 '.h': 'text/plain',
843 })
844
845
846# Utilities for CGIHTTPRequestHandler
847
Senthil Kumarand70846b2012-04-12 02:34:32 +0800848def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000849 """
850 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000851 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000852
853 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800854 The utility of this function is limited to is_cgi method and helps
855 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000856
Martin Pantercb29e8c2015-10-03 05:55:46 +0000857 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000858
859 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800860
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000861 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000862 # Query component should not be involved.
863 path, _, query = path.partition('?')
864 path = urllib.parse.unquote(path)
865
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000866 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
867 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800868 path_parts = path.split('/')
869 head_parts = []
870 for part in path_parts[:-1]:
871 if part == '..':
872 head_parts.pop() # IndexError if more '..' than prior parts
873 elif part and part != '.':
874 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000875 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800876 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800877 if tail_part:
878 if tail_part == '..':
879 head_parts.pop()
880 tail_part = ''
881 elif tail_part == '.':
882 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000883 else:
884 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800885
Martin Pantercb29e8c2015-10-03 05:55:46 +0000886 if query:
887 tail_part = '?'.join((tail_part, query))
888
Senthil Kumarand70846b2012-04-12 02:34:32 +0800889 splitpath = ('/' + '/'.join(head_parts), tail_part)
890 collapsed_path = "/".join(splitpath)
891
892 return collapsed_path
893
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000894
895
Georg Brandl24420152008-05-26 16:32:26 +0000896nobody = None
897
898def nobody_uid():
899 """Internal routine to get nobody's uid"""
900 global nobody
901 if nobody:
902 return nobody
903 try:
904 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400905 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000906 return -1
907 try:
908 nobody = pwd.getpwnam('nobody')[2]
909 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000910 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000911 return nobody
912
913
914def executable(path):
915 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200916 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000917
918
919class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
920
921 """Complete HTTP server with GET, HEAD and POST commands.
922
923 GET and HEAD also support running CGI scripts.
924
925 The POST command is *only* implemented for CGI scripts.
926
927 """
928
929 # Determine platform specifics
930 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000931
932 # Make rfile unbuffered -- we need to read one line and then pass
933 # the rest to a subprocess, so we can't use buffered input.
934 rbufsize = 0
935
936 def do_POST(self):
937 """Serve a POST request.
938
939 This is only implemented for CGI scripts.
940
941 """
942
943 if self.is_cgi():
944 self.run_cgi()
945 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200946 self.send_error(
947 HTTPStatus.NOT_IMPLEMENTED,
948 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000949
950 def send_head(self):
951 """Version of send_head that support CGI scripts"""
952 if self.is_cgi():
953 return self.run_cgi()
954 else:
955 return SimpleHTTPRequestHandler.send_head(self)
956
957 def is_cgi(self):
958 """Test whether self.path corresponds to a CGI script.
959
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000960 Returns True and updates the cgi_info attribute to the tuple
961 (dir, rest) if self.path requires running a CGI script.
962 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000963
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000964 If any exception is raised, the caller should assume that
965 self.path was rejected as invalid and act accordingly.
966
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000967 The default implementation tests whether the normalized url
968 path begins with one of the strings in self.cgi_directories
969 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000970
971 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000972 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +0800973 dir_sep = collapsed_path.find('/', 1)
974 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800975 if head in self.cgi_directories:
976 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000977 return True
Georg Brandl24420152008-05-26 16:32:26 +0000978 return False
979
Senthil Kumarand70846b2012-04-12 02:34:32 +0800980
Georg Brandl24420152008-05-26 16:32:26 +0000981 cgi_directories = ['/cgi-bin', '/htbin']
982
983 def is_executable(self, path):
984 """Test whether argument path is an executable file."""
985 return executable(path)
986
987 def is_python(self, path):
988 """Test whether argument path is a Python script."""
989 head, tail = os.path.splitext(path)
990 return tail.lower() in (".py", ".pyw")
991
992 def run_cgi(self):
993 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +0000994 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -0700995 path = dir + '/' + rest
996 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000997 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -0700998 nextdir = path[:i]
999 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +00001000
1001 scriptdir = self.translate_path(nextdir)
1002 if os.path.isdir(scriptdir):
1003 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -07001004 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001005 else:
1006 break
1007
1008 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +00001009 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +00001010
1011 # dissect the part after the directory name into a script name &
1012 # a possible additional path, to be stored in PATH_INFO.
1013 i = rest.find('/')
1014 if i >= 0:
1015 script, rest = rest[:i], rest[i:]
1016 else:
1017 script, rest = rest, ''
1018
1019 scriptname = dir + '/' + script
1020 scriptfile = self.translate_path(scriptname)
1021 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001022 self.send_error(
1023 HTTPStatus.NOT_FOUND,
1024 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001025 return
1026 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001027 self.send_error(
1028 HTTPStatus.FORBIDDEN,
1029 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001030 return
1031 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001032 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001033 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001034 self.send_error(
1035 HTTPStatus.FORBIDDEN,
1036 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001037 return
1038
1039 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1040 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001041 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001042 env['SERVER_SOFTWARE'] = self.version_string()
1043 env['SERVER_NAME'] = self.server.server_name
1044 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1045 env['SERVER_PROTOCOL'] = self.protocol_version
1046 env['SERVER_PORT'] = str(self.server.server_port)
1047 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001048 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001049 env['PATH_INFO'] = uqrest
1050 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1051 env['SCRIPT_NAME'] = scriptname
1052 if query:
1053 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001054 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001055 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001056 if authorization:
1057 authorization = authorization.split()
1058 if len(authorization) == 2:
1059 import base64, binascii
1060 env['AUTH_TYPE'] = authorization[0]
1061 if authorization[0].lower() == "basic":
1062 try:
1063 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001064 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001065 decode('ascii')
1066 except (binascii.Error, UnicodeError):
1067 pass
1068 else:
1069 authorization = authorization.split(':')
1070 if len(authorization) == 2:
1071 env['REMOTE_USER'] = authorization[0]
1072 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001073 if self.headers.get('content-type') is None:
1074 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001075 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001076 env['CONTENT_TYPE'] = self.headers['content-type']
1077 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001078 if length:
1079 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001080 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001081 if referer:
1082 env['HTTP_REFERER'] = referer
1083 accept = []
1084 for line in self.headers.getallmatchingheaders('accept'):
1085 if line[:1] in "\t\n\r ":
1086 accept.append(line.strip())
1087 else:
1088 accept = accept + line[7:].split(',')
1089 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001090 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001091 if ua:
1092 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001093 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001094 cookie_str = ', '.join(co)
1095 if cookie_str:
1096 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001097 # XXX Other HTTP_* headers
1098 # Since we're setting the env in the parent, provide empty
1099 # values to override previously set values
1100 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1101 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1102 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001103
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001104 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001105 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001106
1107 decoded_query = query.replace('+', ' ')
1108
1109 if self.have_fork:
1110 # Unix -- fork as we should
1111 args = [script]
1112 if '=' not in decoded_query:
1113 args.append(decoded_query)
1114 nobody = nobody_uid()
1115 self.wfile.flush() # Always flush before forking
1116 pid = os.fork()
1117 if pid != 0:
1118 # Parent
1119 pid, sts = os.waitpid(pid, 0)
1120 # throw away additional data [see bug #427345]
1121 while select.select([self.rfile], [], [], 0)[0]:
1122 if not self.rfile.read(1):
1123 break
1124 if sts:
1125 self.log_error("CGI script exit status %#x", sts)
1126 return
1127 # Child
1128 try:
1129 try:
1130 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001131 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001132 pass
1133 os.dup2(self.rfile.fileno(), 0)
1134 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001135 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001136 except:
1137 self.server.handle_error(self.request, self.client_address)
1138 os._exit(127)
1139
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001140 else:
1141 # Non-Unix -- use subprocess
1142 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001143 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001144 if self.is_python(scriptfile):
1145 interp = sys.executable
1146 if interp.lower().endswith("w.exe"):
1147 # On Windows, use python.exe, not pythonw.exe
1148 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001149 cmdline = [interp, '-u'] + cmdline
1150 if '=' not in query:
1151 cmdline.append(query)
1152 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001153 try:
1154 nbytes = int(length)
1155 except (TypeError, ValueError):
1156 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001157 p = subprocess.Popen(cmdline,
1158 stdin=subprocess.PIPE,
1159 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001160 stderr=subprocess.PIPE,
1161 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001162 )
Georg Brandl24420152008-05-26 16:32:26 +00001163 if self.command.lower() == "post" and nbytes > 0:
1164 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001165 else:
1166 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001167 # throw away additional data [see bug #427345]
1168 while select.select([self.rfile._sock], [], [], 0)[0]:
1169 if not self.rfile._sock.recv(1):
1170 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001171 stdout, stderr = p.communicate(data)
1172 self.wfile.write(stdout)
1173 if stderr:
1174 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001175 p.stderr.close()
1176 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001177 status = p.returncode
1178 if status:
1179 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001180 else:
1181 self.log_message("CGI script exited OK")
1182
1183
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001184def test(HandlerClass=BaseHTTPRequestHandler,
1185 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001186 """Test the HTTP request handler class.
1187
Robert Collins9644f242015-08-17 12:18:35 +12001188 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001189
1190 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001191 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001192
1193 HandlerClass.protocol_version = protocol
1194 httpd = ServerClass(server_address, HandlerClass)
1195
1196 sa = httpd.socket.getsockname()
1197 print("Serving HTTP on", sa[0], "port", sa[1], "...")
Alexandre Vassalottib5292a22009-04-03 07:16:55 +00001198 try:
1199 httpd.serve_forever()
1200 except KeyboardInterrupt:
1201 print("\nKeyboard interrupt received, exiting.")
1202 httpd.server_close()
1203 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001204
1205if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001206 parser = argparse.ArgumentParser()
1207 parser.add_argument('--cgi', action='store_true',
1208 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001209 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1210 help='Specify alternate bind address '
1211 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001212 parser.add_argument('port', action='store',
1213 default=8000, type=int,
1214 nargs='?',
1215 help='Specify alternate port [default: 8000]')
1216 args = parser.parse_args()
1217 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001218 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001219 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001220 handler_class = SimpleHTTPRequestHandler
1221 test(HandlerClass=handler_class, port=args.port, bind=args.bind)