blob: 7b3e701fb7a76355b1d3cc354fcc09df29c2bab7 [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
Pierre Quentel351adda2017-04-02 12:26:12 +020090import copy
91import datetime
Berker Peksag04bc5b92016-03-14 06:06:03 +020092import email.utils
Georg Brandl1f7fffb2010-10-15 15:57:45 +000093import html
Jeremy Hylton914ab452009-03-27 17:16:06 +000094import http.client
95import io
96import mimetypes
97import os
98import posixpath
99import select
100import shutil
101import socket # For gethostbyaddr()
102import socketserver
103import sys
104import time
105import urllib.parse
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800106
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200107from http import HTTPStatus
108
Georg Brandl24420152008-05-26 16:32:26 +0000109
110# Default error message template
111DEFAULT_ERROR_MESSAGE = """\
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800112<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
113 "http://www.w3.org/TR/html4/strict.dtd">
Ezio Melottica897e92011-11-02 19:33:29 +0200114<html>
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800115 <head>
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800116 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800117 <title>Error response</title>
118 </head>
119 <body>
120 <h1>Error response</h1>
121 <p>Error code: %(code)d</p>
122 <p>Message: %(message)s.</p>
123 <p>Error code explanation: %(code)s - %(explain)s.</p>
124 </body>
125</html>
Georg Brandl24420152008-05-26 16:32:26 +0000126"""
127
128DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8"
129
Georg Brandl24420152008-05-26 16:32:26 +0000130class HTTPServer(socketserver.TCPServer):
131
132 allow_reuse_address = 1 # Seems to make sense in testing environment
133
134 def server_bind(self):
135 """Override server_bind to store the server name."""
136 socketserver.TCPServer.server_bind(self)
Martin Panter50badad2016-04-03 01:28:53 +0000137 host, port = self.server_address[:2]
Georg Brandl24420152008-05-26 16:32:26 +0000138 self.server_name = socket.getfqdn(host)
139 self.server_port = port
140
141
142class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
143
144 """HTTP request handler base class.
145
146 The following explanation of HTTP serves to guide you through the
147 code as well as to expose any misunderstandings I may have about
148 HTTP (so you don't need to read the code to figure out I'm wrong
149 :-).
150
151 HTTP (HyperText Transfer Protocol) is an extensible protocol on
152 top of a reliable stream transport (e.g. TCP/IP). The protocol
153 recognizes three parts to a request:
154
155 1. One line identifying the request type and path
156 2. An optional set of RFC-822-style headers
157 3. An optional data part
158
159 The headers and data are separated by a blank line.
160
161 The first line of the request has the form
162
163 <command> <path> <version>
164
165 where <command> is a (case-sensitive) keyword such as GET or POST,
166 <path> is a string containing path information for the request,
167 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
168 <path> is encoded using the URL encoding scheme (using %xx to signify
169 the ASCII character with hex code xx).
170
171 The specification specifies that lines are separated by CRLF but
172 for compatibility with the widest range of clients recommends
173 servers also handle LF. Similarly, whitespace in the request line
174 is treated sensibly (allowing multiple spaces between components
175 and allowing trailing whitespace).
176
177 Similarly, for output, lines ought to be separated by CRLF pairs
178 but most clients grok LF characters just fine.
179
180 If the first line of the request has the form
181
182 <command> <path>
183
184 (i.e. <version> is left out) then this is assumed to be an HTTP
185 0.9 request; this form has no optional headers and data part and
186 the reply consists of just the data.
187
188 The reply form of the HTTP 1.x protocol again has three parts:
189
190 1. One line giving the response code
191 2. An optional set of RFC-822-style headers
192 3. The data
193
194 Again, the headers and data are separated by a blank line.
195
196 The response code line has the form
197
198 <version> <responsecode> <responsestring>
199
200 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
201 <responsecode> is a 3-digit response code indicating success or
202 failure of the request, and <responsestring> is an optional
203 human-readable string explaining what the response code means.
204
205 This server parses the request and the headers, and then calls a
206 function specific to the request type (<command>). Specifically,
207 a request SPAM will be handled by a method do_SPAM(). If no
208 such method exists the server sends an error response to the
209 client. If it exists, it is called with no arguments:
210
211 do_SPAM()
212
213 Note that the request name is case sensitive (i.e. SPAM and spam
214 are different requests).
215
216 The various request details are stored in instance variables:
217
218 - client_address is the client IP address in the form (host,
219 port);
220
221 - command, path and version are the broken-down request line;
222
Barry Warsaw820c1202008-06-12 04:06:45 +0000223 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000224 class) containing the header information;
225
226 - rfile is a file object open for reading positioned at the
227 start of the optional input data part;
228
229 - wfile is a file object open for writing.
230
231 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
232
233 The first thing to be written must be the response line. Then
234 follow 0 or more header lines, then a blank line, and then the
235 actual data (if any). The meaning of the header lines depends on
236 the command executed by the server; in most cases, when data is
237 returned, there should be at least one header line of the form
238
239 Content-type: <type>/<subtype>
240
241 where <type> and <subtype> should be registered MIME types,
242 e.g. "text/html" or "text/plain".
243
244 """
245
246 # The Python system version, truncated to its first component.
247 sys_version = "Python/" + sys.version.split()[0]
248
249 # The server software version. You may want to override this.
250 # The format is multiple whitespace-separated strings,
251 # where each string is of the form name[/version].
252 server_version = "BaseHTTP/" + __version__
253
254 error_message_format = DEFAULT_ERROR_MESSAGE
255 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
256
257 # The default request version. This only affects responses up until
258 # the point where the request line is parsed, so it mainly decides what
259 # the client gets back when sending a malformed request line.
260 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
261 default_request_version = "HTTP/0.9"
262
263 def parse_request(self):
264 """Parse a request (internal).
265
266 The request should be stored in self.raw_requestline; the results
267 are in self.command, self.path, self.request_version and
268 self.headers.
269
Martin Pantere82338d2016-11-19 01:06:37 +0000270 Return True for success, False for failure; on failure, any relevant
271 error response has already been sent back.
Georg Brandl24420152008-05-26 16:32:26 +0000272
273 """
274 self.command = None # set in case of error on the first line
275 self.request_version = version = self.default_request_version
Benjamin Peterson70e28472015-02-17 21:11:10 -0500276 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000277 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800278 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000279 self.requestline = requestline
280 words = requestline.split()
Martin Pantere82338d2016-11-19 01:06:37 +0000281 if len(words) == 0:
282 return False
283
284 if len(words) >= 3: # Enough to determine protocol version
285 version = words[-1]
Georg Brandl24420152008-05-26 16:32:26 +0000286 try:
Martin Pantere82338d2016-11-19 01:06:37 +0000287 if not version.startswith('HTTP/'):
Martin Panter50badad2016-04-03 01:28:53 +0000288 raise ValueError
Georg Brandl24420152008-05-26 16:32:26 +0000289 base_version_number = version.split('/', 1)[1]
290 version_number = base_version_number.split(".")
291 # RFC 2145 section 3.1 says there can be only one "." and
292 # - major and minor numbers MUST be treated as
293 # separate integers;
294 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
295 # turn is lower than HTTP/12.3;
296 # - Leading zeros MUST be ignored by recipients.
297 if len(version_number) != 2:
298 raise ValueError
299 version_number = int(version_number[0]), int(version_number[1])
300 except (ValueError, IndexError):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200301 self.send_error(
302 HTTPStatus.BAD_REQUEST,
303 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000304 return False
305 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
Benjamin Peterson70e28472015-02-17 21:11:10 -0500306 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000307 if version_number >= (2, 0):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200308 self.send_error(
309 HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
Martin Panter50badad2016-04-03 01:28:53 +0000310 "Invalid HTTP version (%s)" % base_version_number)
Georg Brandl24420152008-05-26 16:32:26 +0000311 return False
Martin Pantere82338d2016-11-19 01:06:37 +0000312 self.request_version = version
313
314 if not 2 <= len(words) <= 3:
315 self.send_error(
316 HTTPStatus.BAD_REQUEST,
317 "Bad request syntax (%r)" % requestline)
318 return False
319 command, path = words[:2]
320 if len(words) == 2:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500321 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000322 if command != 'GET':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200323 self.send_error(
324 HTTPStatus.BAD_REQUEST,
325 "Bad HTTP/0.9 request type (%r)" % command)
Georg Brandl24420152008-05-26 16:32:26 +0000326 return False
Martin Pantere82338d2016-11-19 01:06:37 +0000327 self.command, self.path = command, path
Georg Brandl24420152008-05-26 16:32:26 +0000328
329 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000330 try:
331 self.headers = http.client.parse_headers(self.rfile,
332 _class=self.MessageClass)
Martin Panter50badad2016-04-03 01:28:53 +0000333 except http.client.LineTooLong as err:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200334 self.send_error(
Martin Panter50badad2016-04-03 01:28:53 +0000335 HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
336 "Line too long",
337 str(err))
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000338 return False
Martin Panteracc03192016-04-03 00:45:46 +0000339 except http.client.HTTPException as err:
340 self.send_error(
341 HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
342 "Too many headers",
343 str(err)
344 )
345 return False
Georg Brandl24420152008-05-26 16:32:26 +0000346
347 conntype = self.headers.get('Connection', "")
348 if conntype.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500349 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000350 elif (conntype.lower() == 'keep-alive' and
351 self.protocol_version >= "HTTP/1.1"):
Benjamin Peterson70e28472015-02-17 21:11:10 -0500352 self.close_connection = False
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000353 # Examine the headers and look for an Expect directive
354 expect = self.headers.get('Expect', "")
355 if (expect.lower() == "100-continue" and
356 self.protocol_version >= "HTTP/1.1" and
357 self.request_version >= "HTTP/1.1"):
358 if not self.handle_expect_100():
359 return False
360 return True
361
362 def handle_expect_100(self):
363 """Decide what to do with an "Expect: 100-continue" header.
364
365 If the client is expecting a 100 Continue response, we must
366 respond with either a 100 Continue or a final response before
367 waiting for the request body. The default is to always respond
368 with a 100 Continue. You can behave differently (for example,
369 reject unauthorized requests) by overriding this method.
370
371 This method should either return True (possibly after sending
372 a 100 Continue response) or send an error response and return
373 False.
374
375 """
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200376 self.send_response_only(HTTPStatus.CONTINUE)
Benjamin Peterson04424232014-01-18 21:50:18 -0500377 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000378 return True
379
380 def handle_one_request(self):
381 """Handle a single HTTP request.
382
383 You normally don't need to override this method; see the class
384 __doc__ string for information on how to handle specific HTTP
385 commands such as GET and POST.
386
387 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000388 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000389 self.raw_requestline = self.rfile.readline(65537)
390 if len(self.raw_requestline) > 65536:
391 self.requestline = ''
392 self.request_version = ''
393 self.command = ''
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200394 self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
Antoine Pitrouc4924372010-12-16 16:48:36 +0000395 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000396 if not self.raw_requestline:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500397 self.close_connection = True
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000398 return
399 if not self.parse_request():
400 # An error code has been sent, just exit
401 return
402 mname = 'do_' + self.command
403 if not hasattr(self, mname):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200404 self.send_error(
405 HTTPStatus.NOT_IMPLEMENTED,
406 "Unsupported method (%r)" % self.command)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000407 return
408 method = getattr(self, mname)
409 method()
410 self.wfile.flush() #actually send the response if not already done.
411 except socket.timeout as e:
412 #a read or a write timed out. Discard this connection
413 self.log_error("Request timed out: %r", e)
Benjamin Peterson70e28472015-02-17 21:11:10 -0500414 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000415 return
Georg Brandl24420152008-05-26 16:32:26 +0000416
417 def handle(self):
418 """Handle multiple requests if necessary."""
Benjamin Peterson70e28472015-02-17 21:11:10 -0500419 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000420
421 self.handle_one_request()
422 while not self.close_connection:
423 self.handle_one_request()
424
Senthil Kumaran26886442013-03-15 07:53:21 -0700425 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000426 """Send and log an error reply.
427
Senthil Kumaran26886442013-03-15 07:53:21 -0700428 Arguments are
429 * code: an HTTP error code
430 3 digits
431 * message: a simple optional 1 line reason phrase.
432 *( HTAB / SP / VCHAR / %x80-FF )
433 defaults to short entry matching the response code
434 * explain: a detailed message defaults to the long entry
435 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000436
437 This sends an error response (so it must be called before any
438 output has been generated), logs the error, and finally sends
439 a piece of HTML explaining the error to the user.
440
441 """
442
443 try:
444 shortmsg, longmsg = self.responses[code]
445 except KeyError:
446 shortmsg, longmsg = '???', '???'
447 if message is None:
448 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700449 if explain is None:
450 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000451 self.log_error("code %d, message %s", code, message)
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800452 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000453 self.send_header('Connection', 'close')
Martin Pantere42e1292016-06-08 08:29:13 +0000454
455 # Message body is omitted for cases described in:
456 # - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
457 # - RFC7231: 6.3.6. 205(Reset Content)
458 body = None
459 if (code >= 200 and
460 code not in (HTTPStatus.NO_CONTENT,
461 HTTPStatus.RESET_CONTENT,
462 HTTPStatus.NOT_MODIFIED)):
463 # HTML encode to prevent Cross Site Scripting attacks
464 # (see bug #1100201)
465 content = (self.error_message_format % {
466 'code': code,
Martin Panter40de69a2016-06-08 09:45:58 +0000467 'message': html.escape(message, quote=False),
468 'explain': html.escape(explain, quote=False)
Martin Pantere42e1292016-06-08 08:29:13 +0000469 })
470 body = content.encode('UTF-8', 'replace')
471 self.send_header("Content-Type", self.error_content_type)
472 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000473 self.end_headers()
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200474
Martin Pantere42e1292016-06-08 08:29:13 +0000475 if self.command != 'HEAD' and body:
Senthil Kumaran52d27202012-10-10 23:16:21 -0700476 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000477
478 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800479 """Add the response header to the headers buffer and log the
480 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000481
482 Also send two standard headers with the server software
483 version and the current date.
484
485 """
486 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000487 self.send_response_only(code, message)
488 self.send_header('Server', self.version_string())
489 self.send_header('Date', self.date_time_string())
490
491 def send_response_only(self, code, message=None):
492 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000493 if self.request_version != 'HTTP/0.9':
Martin Panter50badad2016-04-03 01:28:53 +0000494 if message is None:
495 if code in self.responses:
496 message = self.responses[code][0]
497 else:
498 message = ''
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800499 if not hasattr(self, '_headers_buffer'):
500 self._headers_buffer = []
501 self._headers_buffer.append(("%s %d %s\r\n" %
502 (self.protocol_version, code, message)).encode(
503 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000504
505 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800506 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000507 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000508 if not hasattr(self, '_headers_buffer'):
509 self._headers_buffer = []
510 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000511 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000512
513 if keyword.lower() == 'connection':
514 if value.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500515 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000516 elif value.lower() == 'keep-alive':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500517 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000518
519 def end_headers(self):
520 """Send the blank line ending the MIME headers."""
521 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000522 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800523 self.flush_headers()
524
525 def flush_headers(self):
526 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000527 self.wfile.write(b"".join(self._headers_buffer))
528 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000529
530 def log_request(self, code='-', size='-'):
531 """Log an accepted request.
532
533 This is called by send_response().
534
535 """
Serhiy Storchakac0a23e62015-03-07 11:51:37 +0200536 if isinstance(code, HTTPStatus):
537 code = code.value
Georg Brandl24420152008-05-26 16:32:26 +0000538 self.log_message('"%s" %s %s',
539 self.requestline, str(code), str(size))
540
541 def log_error(self, format, *args):
542 """Log an error.
543
544 This is called when a request cannot be fulfilled. By
545 default it passes the message on to log_message().
546
547 Arguments are the same as for log_message().
548
549 XXX This should go to the separate error log.
550
551 """
552
553 self.log_message(format, *args)
554
555 def log_message(self, format, *args):
556 """Log an arbitrary message.
557
558 This is used by all other logging functions. Override
559 it if you have specific logging wishes.
560
561 The first argument, FORMAT, is a format string for the
562 message to be logged. If the format string contains
563 any % escapes requiring parameters, they should be
564 specified as subsequent arguments (it's just like
565 printf!).
566
Senthil Kumarandb727b42012-04-29 13:41:03 +0800567 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000568 every message.
569
570 """
571
572 sys.stderr.write("%s - - [%s] %s\n" %
573 (self.address_string(),
574 self.log_date_time_string(),
575 format%args))
576
577 def version_string(self):
578 """Return the server software version string."""
579 return self.server_version + ' ' + self.sys_version
580
581 def date_time_string(self, timestamp=None):
582 """Return the current date and time formatted for a message header."""
583 if timestamp is None:
584 timestamp = time.time()
Berker Peksag04bc5b92016-03-14 06:06:03 +0200585 return email.utils.formatdate(timestamp, usegmt=True)
Georg Brandl24420152008-05-26 16:32:26 +0000586
587 def log_date_time_string(self):
588 """Return the current time formatted for logging."""
589 now = time.time()
590 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
591 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
592 day, self.monthname[month], year, hh, mm, ss)
593 return s
594
595 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
596
597 monthname = [None,
598 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
599 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
600
601 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800602 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000603
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800604 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000605
606 # Essentially static class variables
607
608 # The version of the HTTP protocol we support.
609 # Set this to HTTP/1.1 to enable automatic keepalive
610 protocol_version = "HTTP/1.0"
611
Barry Warsaw820c1202008-06-12 04:06:45 +0000612 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000613 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000614
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200615 # hack to maintain backwards compatibility
Georg Brandl24420152008-05-26 16:32:26 +0000616 responses = {
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200617 v: (v.phrase, v.description)
618 for v in HTTPStatus.__members__.values()
619 }
Georg Brandl24420152008-05-26 16:32:26 +0000620
621
622class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
623
624 """Simple HTTP request handler with GET and HEAD commands.
625
626 This serves files from the current directory and any of its
627 subdirectories. The MIME type for files is determined by
628 calling the .guess_type() method.
629
630 The GET and HEAD requests are identical except that the HEAD
631 request omits the actual contents of the file.
632
633 """
634
635 server_version = "SimpleHTTP/" + __version__
636
637 def do_GET(self):
638 """Serve a GET request."""
639 f = self.send_head()
640 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200641 try:
642 self.copyfile(f, self.wfile)
643 finally:
644 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000645
646 def do_HEAD(self):
647 """Serve a HEAD request."""
648 f = self.send_head()
649 if f:
650 f.close()
651
652 def send_head(self):
653 """Common code for GET and HEAD commands.
654
655 This sends the response code and MIME headers.
656
657 Return value is either a file object (which has to be copied
658 to the outputfile by the caller unless the command was HEAD,
659 and must be closed by the caller under all circumstances), or
660 None, in which case the caller has nothing further to do.
661
662 """
663 path = self.translate_path(self.path)
664 f = None
665 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600666 parts = urllib.parse.urlsplit(self.path)
667 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000668 # redirect browser - doing basically what apache does
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200669 self.send_response(HTTPStatus.MOVED_PERMANENTLY)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600670 new_parts = (parts[0], parts[1], parts[2] + '/',
671 parts[3], parts[4])
672 new_url = urllib.parse.urlunsplit(new_parts)
673 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000674 self.end_headers()
675 return None
676 for index in "index.html", "index.htm":
677 index = os.path.join(path, index)
678 if os.path.exists(index):
679 path = index
680 break
681 else:
682 return self.list_directory(path)
683 ctype = self.guess_type(path)
684 try:
685 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200686 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200687 self.send_error(HTTPStatus.NOT_FOUND, "File not found")
Georg Brandl24420152008-05-26 16:32:26 +0000688 return None
Pierre Quentel351adda2017-04-02 12:26:12 +0200689
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200690 try:
Pierre Quentel351adda2017-04-02 12:26:12 +0200691 fs = os.fstat(f.fileno())
692 # Use browser cache if possible
693 if ("If-Modified-Since" in self.headers
694 and "If-None-Match" not in self.headers):
695 # compare If-Modified-Since and time of last file modification
696 try:
697 ims = email.utils.parsedate_to_datetime(
698 self.headers["If-Modified-Since"])
699 except (TypeError, IndexError, OverflowError, ValueError):
700 # ignore ill-formed values
701 pass
702 else:
703 if ims.tzinfo is None:
704 # obsolete format with no timezone, cf.
705 # https://tools.ietf.org/html/rfc7231#section-7.1.1.1
706 ims = ims.replace(tzinfo=datetime.timezone.utc)
707 if ims.tzinfo is datetime.timezone.utc:
708 # compare to UTC datetime of last modification
709 last_modif = datetime.datetime.fromtimestamp(
710 fs.st_mtime, datetime.timezone.utc)
711 # remove microseconds, like in If-Modified-Since
712 last_modif = last_modif.replace(microsecond=0)
713
714 if last_modif <= ims:
715 self.send_response(HTTPStatus.NOT_MODIFIED)
716 self.end_headers()
717 f.close()
718 return None
719
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200720 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200721 self.send_header("Content-type", ctype)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200722 self.send_header("Content-Length", str(fs[6]))
Pierre Quentel351adda2017-04-02 12:26:12 +0200723 self.send_header("Last-Modified",
724 self.date_time_string(fs.st_mtime))
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200725 self.end_headers()
726 return f
727 except:
728 f.close()
729 raise
Georg Brandl24420152008-05-26 16:32:26 +0000730
731 def list_directory(self, path):
732 """Helper to produce a directory listing (absent index.html).
733
734 Return value is either a file object, or None (indicating an
735 error). In either case, the headers are sent, making the
736 interface the same as for send_head().
737
738 """
739 try:
740 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200741 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200742 self.send_error(
743 HTTPStatus.NOT_FOUND,
744 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000745 return None
746 list.sort(key=lambda a: a.lower())
747 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300748 try:
749 displaypath = urllib.parse.unquote(self.path,
750 errors='surrogatepass')
751 except UnicodeDecodeError:
752 displaypath = urllib.parse.unquote(path)
Martin Panterda3bb382016-04-11 00:40:08 +0000753 displaypath = html.escape(displaypath, quote=False)
Ezio Melottica897e92011-11-02 19:33:29 +0200754 enc = sys.getfilesystemencoding()
755 title = 'Directory listing for %s' % displaypath
756 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
757 '"http://www.w3.org/TR/html4/strict.dtd">')
758 r.append('<html>\n<head>')
759 r.append('<meta http-equiv="Content-Type" '
760 'content="text/html; charset=%s">' % enc)
761 r.append('<title>%s</title>\n</head>' % title)
762 r.append('<body>\n<h1>%s</h1>' % title)
763 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000764 for name in list:
765 fullname = os.path.join(path, name)
766 displayname = linkname = name
767 # Append / for directories or @ for symbolic links
768 if os.path.isdir(fullname):
769 displayname = name + "/"
770 linkname = name + "/"
771 if os.path.islink(fullname):
772 displayname = name + "@"
773 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200774 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300775 % (urllib.parse.quote(linkname,
776 errors='surrogatepass'),
Martin Panterda3bb382016-04-11 00:40:08 +0000777 html.escape(displayname, quote=False)))
Ezio Melottica897e92011-11-02 19:33:29 +0200778 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300779 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000780 f = io.BytesIO()
781 f.write(encoded)
782 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200783 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000784 self.send_header("Content-type", "text/html; charset=%s" % enc)
785 self.send_header("Content-Length", str(len(encoded)))
786 self.end_headers()
787 return f
788
789 def translate_path(self, path):
790 """Translate a /-separated PATH to the local filename syntax.
791
792 Components that mean special things to the local file system
793 (e.g. drive or directory names) are ignored. (XXX They should
794 probably be diagnosed.)
795
796 """
797 # abandon query parameters
798 path = path.split('?',1)[0]
799 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700800 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700801 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300802 try:
803 path = urllib.parse.unquote(path, errors='surrogatepass')
804 except UnicodeDecodeError:
805 path = urllib.parse.unquote(path)
806 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000807 words = path.split('/')
808 words = filter(None, words)
809 path = os.getcwd()
810 for word in words:
Martin Panterd274b3f2016-04-18 03:45:18 +0000811 if os.path.dirname(word) or word in (os.curdir, os.pardir):
812 # Ignore components that are not a simple file/directory name
813 continue
Georg Brandl24420152008-05-26 16:32:26 +0000814 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700815 if trailing_slash:
816 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000817 return path
818
819 def copyfile(self, source, outputfile):
820 """Copy all data between two file objects.
821
822 The SOURCE argument is a file object open for reading
823 (or anything with a read() method) and the DESTINATION
824 argument is a file object open for writing (or
825 anything with a write() method).
826
827 The only reason for overriding this would be to change
828 the block size or perhaps to replace newlines by CRLF
829 -- note however that this the default server uses this
830 to copy binary data as well.
831
832 """
833 shutil.copyfileobj(source, outputfile)
834
835 def guess_type(self, path):
836 """Guess the type of a file.
837
838 Argument is a PATH (a filename).
839
840 Return value is a string of the form type/subtype,
841 usable for a MIME Content-type header.
842
843 The default implementation looks the file's extension
844 up in the table self.extensions_map, using application/octet-stream
845 as a default; however it would be permissible (if
846 slow) to look inside the data to make a better guess.
847
848 """
849
850 base, ext = posixpath.splitext(path)
851 if ext in self.extensions_map:
852 return self.extensions_map[ext]
853 ext = ext.lower()
854 if ext in self.extensions_map:
855 return self.extensions_map[ext]
856 else:
857 return self.extensions_map['']
858
859 if not mimetypes.inited:
860 mimetypes.init() # try to read system mime.types
861 extensions_map = mimetypes.types_map.copy()
862 extensions_map.update({
863 '': 'application/octet-stream', # Default
864 '.py': 'text/plain',
865 '.c': 'text/plain',
866 '.h': 'text/plain',
867 })
868
869
870# Utilities for CGIHTTPRequestHandler
871
Senthil Kumarand70846b2012-04-12 02:34:32 +0800872def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000873 """
874 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000875 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000876
877 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800878 The utility of this function is limited to is_cgi method and helps
879 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000880
Martin Pantercb29e8c2015-10-03 05:55:46 +0000881 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000882
883 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800884
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000885 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000886 # Query component should not be involved.
887 path, _, query = path.partition('?')
888 path = urllib.parse.unquote(path)
889
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000890 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
891 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800892 path_parts = path.split('/')
893 head_parts = []
894 for part in path_parts[:-1]:
895 if part == '..':
896 head_parts.pop() # IndexError if more '..' than prior parts
897 elif part and part != '.':
898 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000899 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800900 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800901 if tail_part:
902 if tail_part == '..':
903 head_parts.pop()
904 tail_part = ''
905 elif tail_part == '.':
906 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000907 else:
908 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800909
Martin Pantercb29e8c2015-10-03 05:55:46 +0000910 if query:
911 tail_part = '?'.join((tail_part, query))
912
Senthil Kumarand70846b2012-04-12 02:34:32 +0800913 splitpath = ('/' + '/'.join(head_parts), tail_part)
914 collapsed_path = "/".join(splitpath)
915
916 return collapsed_path
917
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000918
919
Georg Brandl24420152008-05-26 16:32:26 +0000920nobody = None
921
922def nobody_uid():
923 """Internal routine to get nobody's uid"""
924 global nobody
925 if nobody:
926 return nobody
927 try:
928 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400929 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000930 return -1
931 try:
932 nobody = pwd.getpwnam('nobody')[2]
933 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000934 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000935 return nobody
936
937
938def executable(path):
939 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200940 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000941
942
943class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
944
945 """Complete HTTP server with GET, HEAD and POST commands.
946
947 GET and HEAD also support running CGI scripts.
948
949 The POST command is *only* implemented for CGI scripts.
950
951 """
952
953 # Determine platform specifics
954 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000955
956 # Make rfile unbuffered -- we need to read one line and then pass
957 # the rest to a subprocess, so we can't use buffered input.
958 rbufsize = 0
959
960 def do_POST(self):
961 """Serve a POST request.
962
963 This is only implemented for CGI scripts.
964
965 """
966
967 if self.is_cgi():
968 self.run_cgi()
969 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200970 self.send_error(
971 HTTPStatus.NOT_IMPLEMENTED,
972 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000973
974 def send_head(self):
975 """Version of send_head that support CGI scripts"""
976 if self.is_cgi():
977 return self.run_cgi()
978 else:
979 return SimpleHTTPRequestHandler.send_head(self)
980
981 def is_cgi(self):
982 """Test whether self.path corresponds to a CGI script.
983
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000984 Returns True and updates the cgi_info attribute to the tuple
985 (dir, rest) if self.path requires running a CGI script.
986 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000987
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000988 If any exception is raised, the caller should assume that
989 self.path was rejected as invalid and act accordingly.
990
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000991 The default implementation tests whether the normalized url
992 path begins with one of the strings in self.cgi_directories
993 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000994
995 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000996 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +0800997 dir_sep = collapsed_path.find('/', 1)
998 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800999 if head in self.cgi_directories:
1000 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +00001001 return True
Georg Brandl24420152008-05-26 16:32:26 +00001002 return False
1003
Senthil Kumarand70846b2012-04-12 02:34:32 +08001004
Georg Brandl24420152008-05-26 16:32:26 +00001005 cgi_directories = ['/cgi-bin', '/htbin']
1006
1007 def is_executable(self, path):
1008 """Test whether argument path is an executable file."""
1009 return executable(path)
1010
1011 def is_python(self, path):
1012 """Test whether argument path is a Python script."""
1013 head, tail = os.path.splitext(path)
1014 return tail.lower() in (".py", ".pyw")
1015
1016 def run_cgi(self):
1017 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +00001018 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -07001019 path = dir + '/' + rest
1020 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001021 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -07001022 nextdir = path[:i]
1023 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +00001024
1025 scriptdir = self.translate_path(nextdir)
1026 if os.path.isdir(scriptdir):
1027 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -07001028 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001029 else:
1030 break
1031
1032 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +00001033 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +00001034
1035 # dissect the part after the directory name into a script name &
1036 # a possible additional path, to be stored in PATH_INFO.
1037 i = rest.find('/')
1038 if i >= 0:
1039 script, rest = rest[:i], rest[i:]
1040 else:
1041 script, rest = rest, ''
1042
1043 scriptname = dir + '/' + script
1044 scriptfile = self.translate_path(scriptname)
1045 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001046 self.send_error(
1047 HTTPStatus.NOT_FOUND,
1048 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001049 return
1050 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001051 self.send_error(
1052 HTTPStatus.FORBIDDEN,
1053 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001054 return
1055 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001056 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001057 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001058 self.send_error(
1059 HTTPStatus.FORBIDDEN,
1060 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001061 return
1062
1063 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1064 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001065 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001066 env['SERVER_SOFTWARE'] = self.version_string()
1067 env['SERVER_NAME'] = self.server.server_name
1068 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1069 env['SERVER_PROTOCOL'] = self.protocol_version
1070 env['SERVER_PORT'] = str(self.server.server_port)
1071 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001072 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001073 env['PATH_INFO'] = uqrest
1074 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1075 env['SCRIPT_NAME'] = scriptname
1076 if query:
1077 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001078 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001079 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001080 if authorization:
1081 authorization = authorization.split()
1082 if len(authorization) == 2:
1083 import base64, binascii
1084 env['AUTH_TYPE'] = authorization[0]
1085 if authorization[0].lower() == "basic":
1086 try:
1087 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001088 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001089 decode('ascii')
1090 except (binascii.Error, UnicodeError):
1091 pass
1092 else:
1093 authorization = authorization.split(':')
1094 if len(authorization) == 2:
1095 env['REMOTE_USER'] = authorization[0]
1096 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001097 if self.headers.get('content-type') is None:
1098 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001099 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001100 env['CONTENT_TYPE'] = self.headers['content-type']
1101 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001102 if length:
1103 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001104 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001105 if referer:
1106 env['HTTP_REFERER'] = referer
1107 accept = []
1108 for line in self.headers.getallmatchingheaders('accept'):
1109 if line[:1] in "\t\n\r ":
1110 accept.append(line.strip())
1111 else:
1112 accept = accept + line[7:].split(',')
1113 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001114 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001115 if ua:
1116 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001117 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001118 cookie_str = ', '.join(co)
1119 if cookie_str:
1120 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001121 # XXX Other HTTP_* headers
1122 # Since we're setting the env in the parent, provide empty
1123 # values to override previously set values
1124 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1125 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1126 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001127
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001128 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001129 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001130
1131 decoded_query = query.replace('+', ' ')
1132
1133 if self.have_fork:
1134 # Unix -- fork as we should
1135 args = [script]
1136 if '=' not in decoded_query:
1137 args.append(decoded_query)
1138 nobody = nobody_uid()
1139 self.wfile.flush() # Always flush before forking
1140 pid = os.fork()
1141 if pid != 0:
1142 # Parent
1143 pid, sts = os.waitpid(pid, 0)
1144 # throw away additional data [see bug #427345]
1145 while select.select([self.rfile], [], [], 0)[0]:
1146 if not self.rfile.read(1):
1147 break
1148 if sts:
1149 self.log_error("CGI script exit status %#x", sts)
1150 return
1151 # Child
1152 try:
1153 try:
1154 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001155 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001156 pass
1157 os.dup2(self.rfile.fileno(), 0)
1158 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001159 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001160 except:
1161 self.server.handle_error(self.request, self.client_address)
1162 os._exit(127)
1163
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001164 else:
1165 # Non-Unix -- use subprocess
1166 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001167 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001168 if self.is_python(scriptfile):
1169 interp = sys.executable
1170 if interp.lower().endswith("w.exe"):
1171 # On Windows, use python.exe, not pythonw.exe
1172 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001173 cmdline = [interp, '-u'] + cmdline
1174 if '=' not in query:
1175 cmdline.append(query)
1176 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001177 try:
1178 nbytes = int(length)
1179 except (TypeError, ValueError):
1180 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001181 p = subprocess.Popen(cmdline,
1182 stdin=subprocess.PIPE,
1183 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001184 stderr=subprocess.PIPE,
1185 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001186 )
Georg Brandl24420152008-05-26 16:32:26 +00001187 if self.command.lower() == "post" and nbytes > 0:
1188 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001189 else:
1190 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001191 # throw away additional data [see bug #427345]
1192 while select.select([self.rfile._sock], [], [], 0)[0]:
1193 if not self.rfile._sock.recv(1):
1194 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001195 stdout, stderr = p.communicate(data)
1196 self.wfile.write(stdout)
1197 if stderr:
1198 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001199 p.stderr.close()
1200 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001201 status = p.returncode
1202 if status:
1203 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001204 else:
1205 self.log_message("CGI script exited OK")
1206
1207
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001208def test(HandlerClass=BaseHTTPRequestHandler,
1209 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001210 """Test the HTTP request handler class.
1211
Robert Collins9644f242015-08-17 12:18:35 +12001212 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001213
1214 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001215 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001216
1217 HandlerClass.protocol_version = protocol
Martin Panter0cab9c12016-04-13 00:36:52 +00001218 with ServerClass(server_address, HandlerClass) as httpd:
1219 sa = httpd.socket.getsockname()
Berker Peksag3a31cca2016-04-29 16:48:11 +03001220 serve_message = "Serving HTTP on {host} port {port} (http://{host}:{port}/) ..."
1221 print(serve_message.format(host=sa[0], port=sa[1]))
Martin Panter0cab9c12016-04-13 00:36:52 +00001222 try:
1223 httpd.serve_forever()
1224 except KeyboardInterrupt:
1225 print("\nKeyboard interrupt received, exiting.")
1226 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001227
1228if __name__ == '__main__':
Serhiy Storchaka7e4db2f2017-05-04 08:17:47 +03001229 import argparse
1230
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001231 parser = argparse.ArgumentParser()
1232 parser.add_argument('--cgi', action='store_true',
1233 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001234 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1235 help='Specify alternate bind address '
1236 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001237 parser.add_argument('port', action='store',
1238 default=8000, type=int,
1239 nargs='?',
1240 help='Specify alternate port [default: 8000]')
1241 args = parser.parse_args()
1242 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001243 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001244 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001245 handler_class = SimpleHTTPRequestHandler
1246 test(HandlerClass=handler_class, port=args.port, bind=args.bind)