blob: 61ddecc7efe4e34cb030c3534ccb7d25b8479a93 [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
Berker Peksag04bc5b92016-03-14 06:06:03 +020090import email.utils
Georg Brandl1f7fffb2010-10-15 15:57:45 +000091import html
Jeremy Hylton914ab452009-03-27 17:16:06 +000092import http.client
93import io
94import mimetypes
95import os
96import posixpath
97import select
98import shutil
99import socket # For gethostbyaddr()
100import socketserver
101import sys
102import time
103import urllib.parse
Senthil Kumaran42713722010-10-03 17:55:45 +0000104import copy
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800105import argparse
106
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
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200689 try:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200690 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200691 self.send_header("Content-type", ctype)
692 fs = os.fstat(f.fileno())
693 self.send_header("Content-Length", str(fs[6]))
694 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
695 self.end_headers()
696 return f
697 except:
698 f.close()
699 raise
Georg Brandl24420152008-05-26 16:32:26 +0000700
701 def list_directory(self, path):
702 """Helper to produce a directory listing (absent index.html).
703
704 Return value is either a file object, or None (indicating an
705 error). In either case, the headers are sent, making the
706 interface the same as for send_head().
707
708 """
709 try:
710 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200711 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200712 self.send_error(
713 HTTPStatus.NOT_FOUND,
714 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000715 return None
716 list.sort(key=lambda a: a.lower())
717 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300718 try:
719 displaypath = urllib.parse.unquote(self.path,
720 errors='surrogatepass')
721 except UnicodeDecodeError:
722 displaypath = urllib.parse.unquote(path)
Martin Panterda3bb382016-04-11 00:40:08 +0000723 displaypath = html.escape(displaypath, quote=False)
Ezio Melottica897e92011-11-02 19:33:29 +0200724 enc = sys.getfilesystemencoding()
725 title = 'Directory listing for %s' % displaypath
726 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
727 '"http://www.w3.org/TR/html4/strict.dtd">')
728 r.append('<html>\n<head>')
729 r.append('<meta http-equiv="Content-Type" '
730 'content="text/html; charset=%s">' % enc)
731 r.append('<title>%s</title>\n</head>' % title)
732 r.append('<body>\n<h1>%s</h1>' % title)
733 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000734 for name in list:
735 fullname = os.path.join(path, name)
736 displayname = linkname = name
737 # Append / for directories or @ for symbolic links
738 if os.path.isdir(fullname):
739 displayname = name + "/"
740 linkname = name + "/"
741 if os.path.islink(fullname):
742 displayname = name + "@"
743 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200744 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300745 % (urllib.parse.quote(linkname,
746 errors='surrogatepass'),
Martin Panterda3bb382016-04-11 00:40:08 +0000747 html.escape(displayname, quote=False)))
Ezio Melottica897e92011-11-02 19:33:29 +0200748 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300749 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000750 f = io.BytesIO()
751 f.write(encoded)
752 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200753 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000754 self.send_header("Content-type", "text/html; charset=%s" % enc)
755 self.send_header("Content-Length", str(len(encoded)))
756 self.end_headers()
757 return f
758
759 def translate_path(self, path):
760 """Translate a /-separated PATH to the local filename syntax.
761
762 Components that mean special things to the local file system
763 (e.g. drive or directory names) are ignored. (XXX They should
764 probably be diagnosed.)
765
766 """
767 # abandon query parameters
768 path = path.split('?',1)[0]
769 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700770 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700771 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300772 try:
773 path = urllib.parse.unquote(path, errors='surrogatepass')
774 except UnicodeDecodeError:
775 path = urllib.parse.unquote(path)
776 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000777 words = path.split('/')
778 words = filter(None, words)
779 path = os.getcwd()
780 for word in words:
Martin Panterd274b3f2016-04-18 03:45:18 +0000781 if os.path.dirname(word) or word in (os.curdir, os.pardir):
782 # Ignore components that are not a simple file/directory name
783 continue
Georg Brandl24420152008-05-26 16:32:26 +0000784 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700785 if trailing_slash:
786 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000787 return path
788
789 def copyfile(self, source, outputfile):
790 """Copy all data between two file objects.
791
792 The SOURCE argument is a file object open for reading
793 (or anything with a read() method) and the DESTINATION
794 argument is a file object open for writing (or
795 anything with a write() method).
796
797 The only reason for overriding this would be to change
798 the block size or perhaps to replace newlines by CRLF
799 -- note however that this the default server uses this
800 to copy binary data as well.
801
802 """
803 shutil.copyfileobj(source, outputfile)
804
805 def guess_type(self, path):
806 """Guess the type of a file.
807
808 Argument is a PATH (a filename).
809
810 Return value is a string of the form type/subtype,
811 usable for a MIME Content-type header.
812
813 The default implementation looks the file's extension
814 up in the table self.extensions_map, using application/octet-stream
815 as a default; however it would be permissible (if
816 slow) to look inside the data to make a better guess.
817
818 """
819
820 base, ext = posixpath.splitext(path)
821 if ext in self.extensions_map:
822 return self.extensions_map[ext]
823 ext = ext.lower()
824 if ext in self.extensions_map:
825 return self.extensions_map[ext]
826 else:
827 return self.extensions_map['']
828
829 if not mimetypes.inited:
830 mimetypes.init() # try to read system mime.types
831 extensions_map = mimetypes.types_map.copy()
832 extensions_map.update({
833 '': 'application/octet-stream', # Default
834 '.py': 'text/plain',
835 '.c': 'text/plain',
836 '.h': 'text/plain',
837 })
838
839
840# Utilities for CGIHTTPRequestHandler
841
Senthil Kumarand70846b2012-04-12 02:34:32 +0800842def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000843 """
844 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000845 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000846
847 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800848 The utility of this function is limited to is_cgi method and helps
849 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000850
Martin Pantercb29e8c2015-10-03 05:55:46 +0000851 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000852
853 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800854
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000855 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000856 # Query component should not be involved.
857 path, _, query = path.partition('?')
858 path = urllib.parse.unquote(path)
859
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000860 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
861 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800862 path_parts = path.split('/')
863 head_parts = []
864 for part in path_parts[:-1]:
865 if part == '..':
866 head_parts.pop() # IndexError if more '..' than prior parts
867 elif part and part != '.':
868 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000869 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800870 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800871 if tail_part:
872 if tail_part == '..':
873 head_parts.pop()
874 tail_part = ''
875 elif tail_part == '.':
876 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000877 else:
878 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800879
Martin Pantercb29e8c2015-10-03 05:55:46 +0000880 if query:
881 tail_part = '?'.join((tail_part, query))
882
Senthil Kumarand70846b2012-04-12 02:34:32 +0800883 splitpath = ('/' + '/'.join(head_parts), tail_part)
884 collapsed_path = "/".join(splitpath)
885
886 return collapsed_path
887
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000888
889
Georg Brandl24420152008-05-26 16:32:26 +0000890nobody = None
891
892def nobody_uid():
893 """Internal routine to get nobody's uid"""
894 global nobody
895 if nobody:
896 return nobody
897 try:
898 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400899 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000900 return -1
901 try:
902 nobody = pwd.getpwnam('nobody')[2]
903 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000904 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000905 return nobody
906
907
908def executable(path):
909 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200910 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000911
912
913class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
914
915 """Complete HTTP server with GET, HEAD and POST commands.
916
917 GET and HEAD also support running CGI scripts.
918
919 The POST command is *only* implemented for CGI scripts.
920
921 """
922
923 # Determine platform specifics
924 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000925
926 # Make rfile unbuffered -- we need to read one line and then pass
927 # the rest to a subprocess, so we can't use buffered input.
928 rbufsize = 0
929
930 def do_POST(self):
931 """Serve a POST request.
932
933 This is only implemented for CGI scripts.
934
935 """
936
937 if self.is_cgi():
938 self.run_cgi()
939 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200940 self.send_error(
941 HTTPStatus.NOT_IMPLEMENTED,
942 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000943
944 def send_head(self):
945 """Version of send_head that support CGI scripts"""
946 if self.is_cgi():
947 return self.run_cgi()
948 else:
949 return SimpleHTTPRequestHandler.send_head(self)
950
951 def is_cgi(self):
952 """Test whether self.path corresponds to a CGI script.
953
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000954 Returns True and updates the cgi_info attribute to the tuple
955 (dir, rest) if self.path requires running a CGI script.
956 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000957
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000958 If any exception is raised, the caller should assume that
959 self.path was rejected as invalid and act accordingly.
960
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000961 The default implementation tests whether the normalized url
962 path begins with one of the strings in self.cgi_directories
963 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000964
965 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000966 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +0800967 dir_sep = collapsed_path.find('/', 1)
968 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800969 if head in self.cgi_directories:
970 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000971 return True
Georg Brandl24420152008-05-26 16:32:26 +0000972 return False
973
Senthil Kumarand70846b2012-04-12 02:34:32 +0800974
Georg Brandl24420152008-05-26 16:32:26 +0000975 cgi_directories = ['/cgi-bin', '/htbin']
976
977 def is_executable(self, path):
978 """Test whether argument path is an executable file."""
979 return executable(path)
980
981 def is_python(self, path):
982 """Test whether argument path is a Python script."""
983 head, tail = os.path.splitext(path)
984 return tail.lower() in (".py", ".pyw")
985
986 def run_cgi(self):
987 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +0000988 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -0700989 path = dir + '/' + rest
990 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000991 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -0700992 nextdir = path[:i]
993 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +0000994
995 scriptdir = self.translate_path(nextdir)
996 if os.path.isdir(scriptdir):
997 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -0700998 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000999 else:
1000 break
1001
1002 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +00001003 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +00001004
1005 # dissect the part after the directory name into a script name &
1006 # a possible additional path, to be stored in PATH_INFO.
1007 i = rest.find('/')
1008 if i >= 0:
1009 script, rest = rest[:i], rest[i:]
1010 else:
1011 script, rest = rest, ''
1012
1013 scriptname = dir + '/' + script
1014 scriptfile = self.translate_path(scriptname)
1015 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001016 self.send_error(
1017 HTTPStatus.NOT_FOUND,
1018 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001019 return
1020 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001021 self.send_error(
1022 HTTPStatus.FORBIDDEN,
1023 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001024 return
1025 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001026 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001027 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001028 self.send_error(
1029 HTTPStatus.FORBIDDEN,
1030 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001031 return
1032
1033 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1034 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001035 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001036 env['SERVER_SOFTWARE'] = self.version_string()
1037 env['SERVER_NAME'] = self.server.server_name
1038 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1039 env['SERVER_PROTOCOL'] = self.protocol_version
1040 env['SERVER_PORT'] = str(self.server.server_port)
1041 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001042 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001043 env['PATH_INFO'] = uqrest
1044 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1045 env['SCRIPT_NAME'] = scriptname
1046 if query:
1047 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001048 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001049 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001050 if authorization:
1051 authorization = authorization.split()
1052 if len(authorization) == 2:
1053 import base64, binascii
1054 env['AUTH_TYPE'] = authorization[0]
1055 if authorization[0].lower() == "basic":
1056 try:
1057 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001058 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001059 decode('ascii')
1060 except (binascii.Error, UnicodeError):
1061 pass
1062 else:
1063 authorization = authorization.split(':')
1064 if len(authorization) == 2:
1065 env['REMOTE_USER'] = authorization[0]
1066 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001067 if self.headers.get('content-type') is None:
1068 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001069 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001070 env['CONTENT_TYPE'] = self.headers['content-type']
1071 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001072 if length:
1073 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001074 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001075 if referer:
1076 env['HTTP_REFERER'] = referer
1077 accept = []
1078 for line in self.headers.getallmatchingheaders('accept'):
1079 if line[:1] in "\t\n\r ":
1080 accept.append(line.strip())
1081 else:
1082 accept = accept + line[7:].split(',')
1083 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001084 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001085 if ua:
1086 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001087 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001088 cookie_str = ', '.join(co)
1089 if cookie_str:
1090 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001091 # XXX Other HTTP_* headers
1092 # Since we're setting the env in the parent, provide empty
1093 # values to override previously set values
1094 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1095 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1096 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001097
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001098 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001099 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001100
1101 decoded_query = query.replace('+', ' ')
1102
1103 if self.have_fork:
1104 # Unix -- fork as we should
1105 args = [script]
1106 if '=' not in decoded_query:
1107 args.append(decoded_query)
1108 nobody = nobody_uid()
1109 self.wfile.flush() # Always flush before forking
1110 pid = os.fork()
1111 if pid != 0:
1112 # Parent
1113 pid, sts = os.waitpid(pid, 0)
1114 # throw away additional data [see bug #427345]
1115 while select.select([self.rfile], [], [], 0)[0]:
1116 if not self.rfile.read(1):
1117 break
1118 if sts:
1119 self.log_error("CGI script exit status %#x", sts)
1120 return
1121 # Child
1122 try:
1123 try:
1124 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001125 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001126 pass
1127 os.dup2(self.rfile.fileno(), 0)
1128 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001129 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001130 except:
1131 self.server.handle_error(self.request, self.client_address)
1132 os._exit(127)
1133
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001134 else:
1135 # Non-Unix -- use subprocess
1136 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001137 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001138 if self.is_python(scriptfile):
1139 interp = sys.executable
1140 if interp.lower().endswith("w.exe"):
1141 # On Windows, use python.exe, not pythonw.exe
1142 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001143 cmdline = [interp, '-u'] + cmdline
1144 if '=' not in query:
1145 cmdline.append(query)
1146 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001147 try:
1148 nbytes = int(length)
1149 except (TypeError, ValueError):
1150 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001151 p = subprocess.Popen(cmdline,
1152 stdin=subprocess.PIPE,
1153 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001154 stderr=subprocess.PIPE,
1155 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001156 )
Georg Brandl24420152008-05-26 16:32:26 +00001157 if self.command.lower() == "post" and nbytes > 0:
1158 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001159 else:
1160 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001161 # throw away additional data [see bug #427345]
1162 while select.select([self.rfile._sock], [], [], 0)[0]:
1163 if not self.rfile._sock.recv(1):
1164 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001165 stdout, stderr = p.communicate(data)
1166 self.wfile.write(stdout)
1167 if stderr:
1168 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001169 p.stderr.close()
1170 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001171 status = p.returncode
1172 if status:
1173 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001174 else:
1175 self.log_message("CGI script exited OK")
1176
1177
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001178def test(HandlerClass=BaseHTTPRequestHandler,
1179 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001180 """Test the HTTP request handler class.
1181
Robert Collins9644f242015-08-17 12:18:35 +12001182 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001183
1184 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001185 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001186
1187 HandlerClass.protocol_version = protocol
Martin Panter0cab9c12016-04-13 00:36:52 +00001188 with ServerClass(server_address, HandlerClass) as httpd:
1189 sa = httpd.socket.getsockname()
Berker Peksag3a31cca2016-04-29 16:48:11 +03001190 serve_message = "Serving HTTP on {host} port {port} (http://{host}:{port}/) ..."
1191 print(serve_message.format(host=sa[0], port=sa[1]))
Martin Panter0cab9c12016-04-13 00:36:52 +00001192 try:
1193 httpd.serve_forever()
1194 except KeyboardInterrupt:
1195 print("\nKeyboard interrupt received, exiting.")
1196 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001197
1198if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001199 parser = argparse.ArgumentParser()
1200 parser.add_argument('--cgi', action='store_true',
1201 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001202 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1203 help='Specify alternate bind address '
1204 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001205 parser.add_argument('port', action='store',
1206 default=8000, type=int,
1207 nargs='?',
1208 help='Specify alternate port [default: 8000]')
1209 args = parser.parse_args()
1210 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001211 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001212 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001213 handler_class = SimpleHTTPRequestHandler
1214 test(HandlerClass=handler_class, port=args.port, bind=args.bind)