blob: b1151a2b654d494d66064963b535003136099a54 [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
Stéphane Wirtela17a2f52017-05-24 09:29:06 +0200106from functools import partial
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800107
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200108from http import HTTPStatus
109
Georg Brandl24420152008-05-26 16:32:26 +0000110
111# Default error message template
112DEFAULT_ERROR_MESSAGE = """\
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800113<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
114 "http://www.w3.org/TR/html4/strict.dtd">
Ezio Melottica897e92011-11-02 19:33:29 +0200115<html>
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800116 <head>
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800117 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800118 <title>Error response</title>
119 </head>
120 <body>
121 <h1>Error response</h1>
122 <p>Error code: %(code)d</p>
123 <p>Message: %(message)s.</p>
124 <p>Error code explanation: %(code)s - %(explain)s.</p>
125 </body>
126</html>
Georg Brandl24420152008-05-26 16:32:26 +0000127"""
128
129DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8"
130
Georg Brandl24420152008-05-26 16:32:26 +0000131class HTTPServer(socketserver.TCPServer):
132
133 allow_reuse_address = 1 # Seems to make sense in testing environment
134
135 def server_bind(self):
136 """Override server_bind to store the server name."""
137 socketserver.TCPServer.server_bind(self)
Martin Panter50badad2016-04-03 01:28:53 +0000138 host, port = self.server_address[:2]
Georg Brandl24420152008-05-26 16:32:26 +0000139 self.server_name = socket.getfqdn(host)
140 self.server_port = port
141
142
143class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
144
145 """HTTP request handler base class.
146
147 The following explanation of HTTP serves to guide you through the
148 code as well as to expose any misunderstandings I may have about
149 HTTP (so you don't need to read the code to figure out I'm wrong
150 :-).
151
152 HTTP (HyperText Transfer Protocol) is an extensible protocol on
153 top of a reliable stream transport (e.g. TCP/IP). The protocol
154 recognizes three parts to a request:
155
156 1. One line identifying the request type and path
157 2. An optional set of RFC-822-style headers
158 3. An optional data part
159
160 The headers and data are separated by a blank line.
161
162 The first line of the request has the form
163
164 <command> <path> <version>
165
166 where <command> is a (case-sensitive) keyword such as GET or POST,
167 <path> is a string containing path information for the request,
168 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
169 <path> is encoded using the URL encoding scheme (using %xx to signify
170 the ASCII character with hex code xx).
171
172 The specification specifies that lines are separated by CRLF but
173 for compatibility with the widest range of clients recommends
174 servers also handle LF. Similarly, whitespace in the request line
175 is treated sensibly (allowing multiple spaces between components
176 and allowing trailing whitespace).
177
178 Similarly, for output, lines ought to be separated by CRLF pairs
179 but most clients grok LF characters just fine.
180
181 If the first line of the request has the form
182
183 <command> <path>
184
185 (i.e. <version> is left out) then this is assumed to be an HTTP
186 0.9 request; this form has no optional headers and data part and
187 the reply consists of just the data.
188
189 The reply form of the HTTP 1.x protocol again has three parts:
190
191 1. One line giving the response code
192 2. An optional set of RFC-822-style headers
193 3. The data
194
195 Again, the headers and data are separated by a blank line.
196
197 The response code line has the form
198
199 <version> <responsecode> <responsestring>
200
201 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
202 <responsecode> is a 3-digit response code indicating success or
203 failure of the request, and <responsestring> is an optional
204 human-readable string explaining what the response code means.
205
206 This server parses the request and the headers, and then calls a
207 function specific to the request type (<command>). Specifically,
208 a request SPAM will be handled by a method do_SPAM(). If no
209 such method exists the server sends an error response to the
210 client. If it exists, it is called with no arguments:
211
212 do_SPAM()
213
214 Note that the request name is case sensitive (i.e. SPAM and spam
215 are different requests).
216
217 The various request details are stored in instance variables:
218
219 - client_address is the client IP address in the form (host,
220 port);
221
222 - command, path and version are the broken-down request line;
223
Barry Warsaw820c1202008-06-12 04:06:45 +0000224 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000225 class) containing the header information;
226
227 - rfile is a file object open for reading positioned at the
228 start of the optional input data part;
229
230 - wfile is a file object open for writing.
231
232 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
233
234 The first thing to be written must be the response line. Then
235 follow 0 or more header lines, then a blank line, and then the
236 actual data (if any). The meaning of the header lines depends on
237 the command executed by the server; in most cases, when data is
238 returned, there should be at least one header line of the form
239
240 Content-type: <type>/<subtype>
241
242 where <type> and <subtype> should be registered MIME types,
243 e.g. "text/html" or "text/plain".
244
245 """
246
247 # The Python system version, truncated to its first component.
248 sys_version = "Python/" + sys.version.split()[0]
249
250 # The server software version. You may want to override this.
251 # The format is multiple whitespace-separated strings,
252 # where each string is of the form name[/version].
253 server_version = "BaseHTTP/" + __version__
254
255 error_message_format = DEFAULT_ERROR_MESSAGE
256 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
257
258 # The default request version. This only affects responses up until
259 # the point where the request line is parsed, so it mainly decides what
260 # the client gets back when sending a malformed request line.
261 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
262 default_request_version = "HTTP/0.9"
263
264 def parse_request(self):
265 """Parse a request (internal).
266
267 The request should be stored in self.raw_requestline; the results
268 are in self.command, self.path, self.request_version and
269 self.headers.
270
Martin Pantere82338d2016-11-19 01:06:37 +0000271 Return True for success, False for failure; on failure, any relevant
272 error response has already been sent back.
Georg Brandl24420152008-05-26 16:32:26 +0000273
274 """
275 self.command = None # set in case of error on the first line
276 self.request_version = version = self.default_request_version
Benjamin Peterson70e28472015-02-17 21:11:10 -0500277 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000278 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800279 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000280 self.requestline = requestline
281 words = requestline.split()
Martin Pantere82338d2016-11-19 01:06:37 +0000282 if len(words) == 0:
283 return False
284
285 if len(words) >= 3: # Enough to determine protocol version
286 version = words[-1]
Georg Brandl24420152008-05-26 16:32:26 +0000287 try:
Martin Pantere82338d2016-11-19 01:06:37 +0000288 if not version.startswith('HTTP/'):
Martin Panter50badad2016-04-03 01:28:53 +0000289 raise ValueError
Georg Brandl24420152008-05-26 16:32:26 +0000290 base_version_number = version.split('/', 1)[1]
291 version_number = base_version_number.split(".")
292 # RFC 2145 section 3.1 says there can be only one "." and
293 # - major and minor numbers MUST be treated as
294 # separate integers;
295 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
296 # turn is lower than HTTP/12.3;
297 # - Leading zeros MUST be ignored by recipients.
298 if len(version_number) != 2:
299 raise ValueError
300 version_number = int(version_number[0]), int(version_number[1])
301 except (ValueError, IndexError):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200302 self.send_error(
303 HTTPStatus.BAD_REQUEST,
304 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000305 return False
306 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
Benjamin Peterson70e28472015-02-17 21:11:10 -0500307 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000308 if version_number >= (2, 0):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200309 self.send_error(
310 HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
Martin Panter50badad2016-04-03 01:28:53 +0000311 "Invalid HTTP version (%s)" % base_version_number)
Georg Brandl24420152008-05-26 16:32:26 +0000312 return False
Martin Pantere82338d2016-11-19 01:06:37 +0000313 self.request_version = version
314
315 if not 2 <= len(words) <= 3:
316 self.send_error(
317 HTTPStatus.BAD_REQUEST,
318 "Bad request syntax (%r)" % requestline)
319 return False
320 command, path = words[:2]
321 if len(words) == 2:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500322 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000323 if command != 'GET':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200324 self.send_error(
325 HTTPStatus.BAD_REQUEST,
326 "Bad HTTP/0.9 request type (%r)" % command)
Georg Brandl24420152008-05-26 16:32:26 +0000327 return False
Martin Pantere82338d2016-11-19 01:06:37 +0000328 self.command, self.path = command, path
Georg Brandl24420152008-05-26 16:32:26 +0000329
330 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000331 try:
332 self.headers = http.client.parse_headers(self.rfile,
333 _class=self.MessageClass)
Martin Panter50badad2016-04-03 01:28:53 +0000334 except http.client.LineTooLong as err:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200335 self.send_error(
Martin Panter50badad2016-04-03 01:28:53 +0000336 HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
337 "Line too long",
338 str(err))
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000339 return False
Martin Panteracc03192016-04-03 00:45:46 +0000340 except http.client.HTTPException as err:
341 self.send_error(
342 HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
343 "Too many headers",
344 str(err)
345 )
346 return False
Georg Brandl24420152008-05-26 16:32:26 +0000347
348 conntype = self.headers.get('Connection', "")
349 if conntype.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500350 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000351 elif (conntype.lower() == 'keep-alive' and
352 self.protocol_version >= "HTTP/1.1"):
Benjamin Peterson70e28472015-02-17 21:11:10 -0500353 self.close_connection = False
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000354 # Examine the headers and look for an Expect directive
355 expect = self.headers.get('Expect', "")
356 if (expect.lower() == "100-continue" and
357 self.protocol_version >= "HTTP/1.1" and
358 self.request_version >= "HTTP/1.1"):
359 if not self.handle_expect_100():
360 return False
361 return True
362
363 def handle_expect_100(self):
364 """Decide what to do with an "Expect: 100-continue" header.
365
366 If the client is expecting a 100 Continue response, we must
367 respond with either a 100 Continue or a final response before
368 waiting for the request body. The default is to always respond
369 with a 100 Continue. You can behave differently (for example,
370 reject unauthorized requests) by overriding this method.
371
372 This method should either return True (possibly after sending
373 a 100 Continue response) or send an error response and return
374 False.
375
376 """
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200377 self.send_response_only(HTTPStatus.CONTINUE)
Benjamin Peterson04424232014-01-18 21:50:18 -0500378 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000379 return True
380
381 def handle_one_request(self):
382 """Handle a single HTTP request.
383
384 You normally don't need to override this method; see the class
385 __doc__ string for information on how to handle specific HTTP
386 commands such as GET and POST.
387
388 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000389 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000390 self.raw_requestline = self.rfile.readline(65537)
391 if len(self.raw_requestline) > 65536:
392 self.requestline = ''
393 self.request_version = ''
394 self.command = ''
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200395 self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
Antoine Pitrouc4924372010-12-16 16:48:36 +0000396 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000397 if not self.raw_requestline:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500398 self.close_connection = True
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000399 return
400 if not self.parse_request():
401 # An error code has been sent, just exit
402 return
403 mname = 'do_' + self.command
404 if not hasattr(self, mname):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200405 self.send_error(
406 HTTPStatus.NOT_IMPLEMENTED,
407 "Unsupported method (%r)" % self.command)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000408 return
409 method = getattr(self, mname)
410 method()
411 self.wfile.flush() #actually send the response if not already done.
412 except socket.timeout as e:
413 #a read or a write timed out. Discard this connection
414 self.log_error("Request timed out: %r", e)
Benjamin Peterson70e28472015-02-17 21:11:10 -0500415 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000416 return
Georg Brandl24420152008-05-26 16:32:26 +0000417
418 def handle(self):
419 """Handle multiple requests if necessary."""
Benjamin Peterson70e28472015-02-17 21:11:10 -0500420 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000421
422 self.handle_one_request()
423 while not self.close_connection:
424 self.handle_one_request()
425
Senthil Kumaran26886442013-03-15 07:53:21 -0700426 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000427 """Send and log an error reply.
428
Senthil Kumaran26886442013-03-15 07:53:21 -0700429 Arguments are
430 * code: an HTTP error code
431 3 digits
432 * message: a simple optional 1 line reason phrase.
433 *( HTAB / SP / VCHAR / %x80-FF )
434 defaults to short entry matching the response code
435 * explain: a detailed message defaults to the long entry
436 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000437
438 This sends an error response (so it must be called before any
439 output has been generated), logs the error, and finally sends
440 a piece of HTML explaining the error to the user.
441
442 """
443
444 try:
445 shortmsg, longmsg = self.responses[code]
446 except KeyError:
447 shortmsg, longmsg = '???', '???'
448 if message is None:
449 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700450 if explain is None:
451 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000452 self.log_error("code %d, message %s", code, message)
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800453 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000454 self.send_header('Connection', 'close')
Martin Pantere42e1292016-06-08 08:29:13 +0000455
456 # Message body is omitted for cases described in:
457 # - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
458 # - RFC7231: 6.3.6. 205(Reset Content)
459 body = None
460 if (code >= 200 and
461 code not in (HTTPStatus.NO_CONTENT,
462 HTTPStatus.RESET_CONTENT,
463 HTTPStatus.NOT_MODIFIED)):
464 # HTML encode to prevent Cross Site Scripting attacks
465 # (see bug #1100201)
466 content = (self.error_message_format % {
467 'code': code,
Martin Panter40de69a2016-06-08 09:45:58 +0000468 'message': html.escape(message, quote=False),
469 'explain': html.escape(explain, quote=False)
Martin Pantere42e1292016-06-08 08:29:13 +0000470 })
471 body = content.encode('UTF-8', 'replace')
472 self.send_header("Content-Type", self.error_content_type)
473 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000474 self.end_headers()
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200475
Martin Pantere42e1292016-06-08 08:29:13 +0000476 if self.command != 'HEAD' and body:
Senthil Kumaran52d27202012-10-10 23:16:21 -0700477 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000478
479 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800480 """Add the response header to the headers buffer and log the
481 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000482
483 Also send two standard headers with the server software
484 version and the current date.
485
486 """
487 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000488 self.send_response_only(code, message)
489 self.send_header('Server', self.version_string())
490 self.send_header('Date', self.date_time_string())
491
492 def send_response_only(self, code, message=None):
493 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000494 if self.request_version != 'HTTP/0.9':
Martin Panter50badad2016-04-03 01:28:53 +0000495 if message is None:
496 if code in self.responses:
497 message = self.responses[code][0]
498 else:
499 message = ''
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800500 if not hasattr(self, '_headers_buffer'):
501 self._headers_buffer = []
502 self._headers_buffer.append(("%s %d %s\r\n" %
503 (self.protocol_version, code, message)).encode(
504 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000505
506 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800507 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000508 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000509 if not hasattr(self, '_headers_buffer'):
510 self._headers_buffer = []
511 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000512 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000513
514 if keyword.lower() == 'connection':
515 if value.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500516 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000517 elif value.lower() == 'keep-alive':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500518 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000519
520 def end_headers(self):
521 """Send the blank line ending the MIME headers."""
522 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000523 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800524 self.flush_headers()
525
526 def flush_headers(self):
527 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000528 self.wfile.write(b"".join(self._headers_buffer))
529 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000530
531 def log_request(self, code='-', size='-'):
532 """Log an accepted request.
533
534 This is called by send_response().
535
536 """
Serhiy Storchakac0a23e62015-03-07 11:51:37 +0200537 if isinstance(code, HTTPStatus):
538 code = code.value
Georg Brandl24420152008-05-26 16:32:26 +0000539 self.log_message('"%s" %s %s',
540 self.requestline, str(code), str(size))
541
542 def log_error(self, format, *args):
543 """Log an error.
544
545 This is called when a request cannot be fulfilled. By
546 default it passes the message on to log_message().
547
548 Arguments are the same as for log_message().
549
550 XXX This should go to the separate error log.
551
552 """
553
554 self.log_message(format, *args)
555
556 def log_message(self, format, *args):
557 """Log an arbitrary message.
558
559 This is used by all other logging functions. Override
560 it if you have specific logging wishes.
561
562 The first argument, FORMAT, is a format string for the
563 message to be logged. If the format string contains
564 any % escapes requiring parameters, they should be
565 specified as subsequent arguments (it's just like
566 printf!).
567
Senthil Kumarandb727b42012-04-29 13:41:03 +0800568 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000569 every message.
570
571 """
572
573 sys.stderr.write("%s - - [%s] %s\n" %
574 (self.address_string(),
575 self.log_date_time_string(),
576 format%args))
577
578 def version_string(self):
579 """Return the server software version string."""
580 return self.server_version + ' ' + self.sys_version
581
582 def date_time_string(self, timestamp=None):
583 """Return the current date and time formatted for a message header."""
584 if timestamp is None:
585 timestamp = time.time()
Berker Peksag04bc5b92016-03-14 06:06:03 +0200586 return email.utils.formatdate(timestamp, usegmt=True)
Georg Brandl24420152008-05-26 16:32:26 +0000587
588 def log_date_time_string(self):
589 """Return the current time formatted for logging."""
590 now = time.time()
591 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
592 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
593 day, self.monthname[month], year, hh, mm, ss)
594 return s
595
596 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
597
598 monthname = [None,
599 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
600 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
601
602 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800603 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000604
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800605 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000606
607 # Essentially static class variables
608
609 # The version of the HTTP protocol we support.
610 # Set this to HTTP/1.1 to enable automatic keepalive
611 protocol_version = "HTTP/1.0"
612
Barry Warsaw820c1202008-06-12 04:06:45 +0000613 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000614 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000615
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200616 # hack to maintain backwards compatibility
Georg Brandl24420152008-05-26 16:32:26 +0000617 responses = {
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200618 v: (v.phrase, v.description)
619 for v in HTTPStatus.__members__.values()
620 }
Georg Brandl24420152008-05-26 16:32:26 +0000621
622
623class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
624
625 """Simple HTTP request handler with GET and HEAD commands.
626
627 This serves files from the current directory and any of its
628 subdirectories. The MIME type for files is determined by
629 calling the .guess_type() method.
630
631 The GET and HEAD requests are identical except that the HEAD
632 request omits the actual contents of the file.
633
634 """
635
636 server_version = "SimpleHTTP/" + __version__
637
Stéphane Wirtela17a2f52017-05-24 09:29:06 +0200638 def __init__(self, *args, directory=None, **kwargs):
639 if directory is None:
640 directory = os.getcwd()
641 self.directory = directory
642 super().__init__(*args, **kwargs)
643
Georg Brandl24420152008-05-26 16:32:26 +0000644 def do_GET(self):
645 """Serve a GET request."""
646 f = self.send_head()
647 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200648 try:
649 self.copyfile(f, self.wfile)
650 finally:
651 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000652
653 def do_HEAD(self):
654 """Serve a HEAD request."""
655 f = self.send_head()
656 if f:
657 f.close()
658
659 def send_head(self):
660 """Common code for GET and HEAD commands.
661
662 This sends the response code and MIME headers.
663
664 Return value is either a file object (which has to be copied
665 to the outputfile by the caller unless the command was HEAD,
666 and must be closed by the caller under all circumstances), or
667 None, in which case the caller has nothing further to do.
668
669 """
670 path = self.translate_path(self.path)
671 f = None
672 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600673 parts = urllib.parse.urlsplit(self.path)
674 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000675 # redirect browser - doing basically what apache does
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200676 self.send_response(HTTPStatus.MOVED_PERMANENTLY)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600677 new_parts = (parts[0], parts[1], parts[2] + '/',
678 parts[3], parts[4])
679 new_url = urllib.parse.urlunsplit(new_parts)
680 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000681 self.end_headers()
682 return None
683 for index in "index.html", "index.htm":
684 index = os.path.join(path, index)
685 if os.path.exists(index):
686 path = index
687 break
688 else:
689 return self.list_directory(path)
690 ctype = self.guess_type(path)
691 try:
692 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200693 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200694 self.send_error(HTTPStatus.NOT_FOUND, "File not found")
Georg Brandl24420152008-05-26 16:32:26 +0000695 return None
Pierre Quentel351adda2017-04-02 12:26:12 +0200696
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200697 try:
Pierre Quentel351adda2017-04-02 12:26:12 +0200698 fs = os.fstat(f.fileno())
699 # Use browser cache if possible
700 if ("If-Modified-Since" in self.headers
701 and "If-None-Match" not in self.headers):
702 # compare If-Modified-Since and time of last file modification
703 try:
704 ims = email.utils.parsedate_to_datetime(
705 self.headers["If-Modified-Since"])
706 except (TypeError, IndexError, OverflowError, ValueError):
707 # ignore ill-formed values
708 pass
709 else:
710 if ims.tzinfo is None:
711 # obsolete format with no timezone, cf.
712 # https://tools.ietf.org/html/rfc7231#section-7.1.1.1
713 ims = ims.replace(tzinfo=datetime.timezone.utc)
714 if ims.tzinfo is datetime.timezone.utc:
715 # compare to UTC datetime of last modification
716 last_modif = datetime.datetime.fromtimestamp(
717 fs.st_mtime, datetime.timezone.utc)
718 # remove microseconds, like in If-Modified-Since
719 last_modif = last_modif.replace(microsecond=0)
720
721 if last_modif <= ims:
722 self.send_response(HTTPStatus.NOT_MODIFIED)
723 self.end_headers()
724 f.close()
725 return None
726
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200727 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200728 self.send_header("Content-type", ctype)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200729 self.send_header("Content-Length", str(fs[6]))
Pierre Quentel351adda2017-04-02 12:26:12 +0200730 self.send_header("Last-Modified",
731 self.date_time_string(fs.st_mtime))
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200732 self.end_headers()
733 return f
734 except:
735 f.close()
736 raise
Georg Brandl24420152008-05-26 16:32:26 +0000737
738 def list_directory(self, path):
739 """Helper to produce a directory listing (absent index.html).
740
741 Return value is either a file object, or None (indicating an
742 error). In either case, the headers are sent, making the
743 interface the same as for send_head().
744
745 """
746 try:
747 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200748 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200749 self.send_error(
750 HTTPStatus.NOT_FOUND,
751 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000752 return None
753 list.sort(key=lambda a: a.lower())
754 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300755 try:
756 displaypath = urllib.parse.unquote(self.path,
757 errors='surrogatepass')
758 except UnicodeDecodeError:
759 displaypath = urllib.parse.unquote(path)
Martin Panterda3bb382016-04-11 00:40:08 +0000760 displaypath = html.escape(displaypath, quote=False)
Ezio Melottica897e92011-11-02 19:33:29 +0200761 enc = sys.getfilesystemencoding()
762 title = 'Directory listing for %s' % displaypath
763 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
764 '"http://www.w3.org/TR/html4/strict.dtd">')
765 r.append('<html>\n<head>')
766 r.append('<meta http-equiv="Content-Type" '
767 'content="text/html; charset=%s">' % enc)
768 r.append('<title>%s</title>\n</head>' % title)
769 r.append('<body>\n<h1>%s</h1>' % title)
770 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000771 for name in list:
772 fullname = os.path.join(path, name)
773 displayname = linkname = name
774 # Append / for directories or @ for symbolic links
775 if os.path.isdir(fullname):
776 displayname = name + "/"
777 linkname = name + "/"
778 if os.path.islink(fullname):
779 displayname = name + "@"
780 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200781 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300782 % (urllib.parse.quote(linkname,
783 errors='surrogatepass'),
Martin Panterda3bb382016-04-11 00:40:08 +0000784 html.escape(displayname, quote=False)))
Ezio Melottica897e92011-11-02 19:33:29 +0200785 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300786 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000787 f = io.BytesIO()
788 f.write(encoded)
789 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200790 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000791 self.send_header("Content-type", "text/html; charset=%s" % enc)
792 self.send_header("Content-Length", str(len(encoded)))
793 self.end_headers()
794 return f
795
796 def translate_path(self, path):
797 """Translate a /-separated PATH to the local filename syntax.
798
799 Components that mean special things to the local file system
800 (e.g. drive or directory names) are ignored. (XXX They should
801 probably be diagnosed.)
802
803 """
804 # abandon query parameters
805 path = path.split('?',1)[0]
806 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700807 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700808 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300809 try:
810 path = urllib.parse.unquote(path, errors='surrogatepass')
811 except UnicodeDecodeError:
812 path = urllib.parse.unquote(path)
813 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000814 words = path.split('/')
815 words = filter(None, words)
Stéphane Wirtela17a2f52017-05-24 09:29:06 +0200816 path = self.directory
Georg Brandl24420152008-05-26 16:32:26 +0000817 for word in words:
Martin Panterd274b3f2016-04-18 03:45:18 +0000818 if os.path.dirname(word) or word in (os.curdir, os.pardir):
819 # Ignore components that are not a simple file/directory name
820 continue
Georg Brandl24420152008-05-26 16:32:26 +0000821 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700822 if trailing_slash:
823 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000824 return path
825
826 def copyfile(self, source, outputfile):
827 """Copy all data between two file objects.
828
829 The SOURCE argument is a file object open for reading
830 (or anything with a read() method) and the DESTINATION
831 argument is a file object open for writing (or
832 anything with a write() method).
833
834 The only reason for overriding this would be to change
835 the block size or perhaps to replace newlines by CRLF
836 -- note however that this the default server uses this
837 to copy binary data as well.
838
839 """
840 shutil.copyfileobj(source, outputfile)
841
842 def guess_type(self, path):
843 """Guess the type of a file.
844
845 Argument is a PATH (a filename).
846
847 Return value is a string of the form type/subtype,
848 usable for a MIME Content-type header.
849
850 The default implementation looks the file's extension
851 up in the table self.extensions_map, using application/octet-stream
852 as a default; however it would be permissible (if
853 slow) to look inside the data to make a better guess.
854
855 """
856
857 base, ext = posixpath.splitext(path)
858 if ext in self.extensions_map:
859 return self.extensions_map[ext]
860 ext = ext.lower()
861 if ext in self.extensions_map:
862 return self.extensions_map[ext]
863 else:
864 return self.extensions_map['']
865
866 if not mimetypes.inited:
867 mimetypes.init() # try to read system mime.types
868 extensions_map = mimetypes.types_map.copy()
869 extensions_map.update({
870 '': 'application/octet-stream', # Default
871 '.py': 'text/plain',
872 '.c': 'text/plain',
873 '.h': 'text/plain',
874 })
875
876
877# Utilities for CGIHTTPRequestHandler
878
Senthil Kumarand70846b2012-04-12 02:34:32 +0800879def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000880 """
881 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000882 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000883
884 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800885 The utility of this function is limited to is_cgi method and helps
886 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000887
Martin Pantercb29e8c2015-10-03 05:55:46 +0000888 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000889
890 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800891
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000892 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000893 # Query component should not be involved.
894 path, _, query = path.partition('?')
895 path = urllib.parse.unquote(path)
896
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000897 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
898 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800899 path_parts = path.split('/')
900 head_parts = []
901 for part in path_parts[:-1]:
902 if part == '..':
903 head_parts.pop() # IndexError if more '..' than prior parts
904 elif part and part != '.':
905 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000906 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800907 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800908 if tail_part:
909 if tail_part == '..':
910 head_parts.pop()
911 tail_part = ''
912 elif tail_part == '.':
913 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000914 else:
915 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800916
Martin Pantercb29e8c2015-10-03 05:55:46 +0000917 if query:
918 tail_part = '?'.join((tail_part, query))
919
Senthil Kumarand70846b2012-04-12 02:34:32 +0800920 splitpath = ('/' + '/'.join(head_parts), tail_part)
921 collapsed_path = "/".join(splitpath)
922
923 return collapsed_path
924
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000925
926
Georg Brandl24420152008-05-26 16:32:26 +0000927nobody = None
928
929def nobody_uid():
930 """Internal routine to get nobody's uid"""
931 global nobody
932 if nobody:
933 return nobody
934 try:
935 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400936 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000937 return -1
938 try:
939 nobody = pwd.getpwnam('nobody')[2]
940 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000941 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000942 return nobody
943
944
945def executable(path):
946 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200947 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000948
949
950class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
951
952 """Complete HTTP server with GET, HEAD and POST commands.
953
954 GET and HEAD also support running CGI scripts.
955
956 The POST command is *only* implemented for CGI scripts.
957
958 """
959
960 # Determine platform specifics
961 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000962
963 # Make rfile unbuffered -- we need to read one line and then pass
964 # the rest to a subprocess, so we can't use buffered input.
965 rbufsize = 0
966
967 def do_POST(self):
968 """Serve a POST request.
969
970 This is only implemented for CGI scripts.
971
972 """
973
974 if self.is_cgi():
975 self.run_cgi()
976 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200977 self.send_error(
978 HTTPStatus.NOT_IMPLEMENTED,
979 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000980
981 def send_head(self):
982 """Version of send_head that support CGI scripts"""
983 if self.is_cgi():
984 return self.run_cgi()
985 else:
986 return SimpleHTTPRequestHandler.send_head(self)
987
988 def is_cgi(self):
989 """Test whether self.path corresponds to a CGI script.
990
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000991 Returns True and updates the cgi_info attribute to the tuple
992 (dir, rest) if self.path requires running a CGI script.
993 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000994
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000995 If any exception is raised, the caller should assume that
996 self.path was rejected as invalid and act accordingly.
997
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000998 The default implementation tests whether the normalized url
999 path begins with one of the strings in self.cgi_directories
1000 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +00001001
1002 """
Martin Pantercb29e8c2015-10-03 05:55:46 +00001003 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +08001004 dir_sep = collapsed_path.find('/', 1)
1005 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +08001006 if head in self.cgi_directories:
1007 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +00001008 return True
Georg Brandl24420152008-05-26 16:32:26 +00001009 return False
1010
Senthil Kumarand70846b2012-04-12 02:34:32 +08001011
Georg Brandl24420152008-05-26 16:32:26 +00001012 cgi_directories = ['/cgi-bin', '/htbin']
1013
1014 def is_executable(self, path):
1015 """Test whether argument path is an executable file."""
1016 return executable(path)
1017
1018 def is_python(self, path):
1019 """Test whether argument path is a Python script."""
1020 head, tail = os.path.splitext(path)
1021 return tail.lower() in (".py", ".pyw")
1022
1023 def run_cgi(self):
1024 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +00001025 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -07001026 path = dir + '/' + rest
1027 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001028 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -07001029 nextdir = path[:i]
1030 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +00001031
1032 scriptdir = self.translate_path(nextdir)
1033 if os.path.isdir(scriptdir):
1034 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -07001035 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001036 else:
1037 break
1038
1039 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +00001040 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +00001041
1042 # dissect the part after the directory name into a script name &
1043 # a possible additional path, to be stored in PATH_INFO.
1044 i = rest.find('/')
1045 if i >= 0:
1046 script, rest = rest[:i], rest[i:]
1047 else:
1048 script, rest = rest, ''
1049
1050 scriptname = dir + '/' + script
1051 scriptfile = self.translate_path(scriptname)
1052 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001053 self.send_error(
1054 HTTPStatus.NOT_FOUND,
1055 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001056 return
1057 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001058 self.send_error(
1059 HTTPStatus.FORBIDDEN,
1060 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001061 return
1062 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001063 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001064 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001065 self.send_error(
1066 HTTPStatus.FORBIDDEN,
1067 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001068 return
1069
1070 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1071 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001072 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001073 env['SERVER_SOFTWARE'] = self.version_string()
1074 env['SERVER_NAME'] = self.server.server_name
1075 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1076 env['SERVER_PROTOCOL'] = self.protocol_version
1077 env['SERVER_PORT'] = str(self.server.server_port)
1078 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001079 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001080 env['PATH_INFO'] = uqrest
1081 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1082 env['SCRIPT_NAME'] = scriptname
1083 if query:
1084 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001085 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001086 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001087 if authorization:
1088 authorization = authorization.split()
1089 if len(authorization) == 2:
1090 import base64, binascii
1091 env['AUTH_TYPE'] = authorization[0]
1092 if authorization[0].lower() == "basic":
1093 try:
1094 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001095 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001096 decode('ascii')
1097 except (binascii.Error, UnicodeError):
1098 pass
1099 else:
1100 authorization = authorization.split(':')
1101 if len(authorization) == 2:
1102 env['REMOTE_USER'] = authorization[0]
1103 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001104 if self.headers.get('content-type') is None:
1105 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001106 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001107 env['CONTENT_TYPE'] = self.headers['content-type']
1108 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001109 if length:
1110 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001111 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001112 if referer:
1113 env['HTTP_REFERER'] = referer
1114 accept = []
1115 for line in self.headers.getallmatchingheaders('accept'):
1116 if line[:1] in "\t\n\r ":
1117 accept.append(line.strip())
1118 else:
1119 accept = accept + line[7:].split(',')
1120 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001121 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001122 if ua:
1123 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001124 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001125 cookie_str = ', '.join(co)
1126 if cookie_str:
1127 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001128 # XXX Other HTTP_* headers
1129 # Since we're setting the env in the parent, provide empty
1130 # values to override previously set values
1131 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1132 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1133 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001134
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001135 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001136 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001137
1138 decoded_query = query.replace('+', ' ')
1139
1140 if self.have_fork:
1141 # Unix -- fork as we should
1142 args = [script]
1143 if '=' not in decoded_query:
1144 args.append(decoded_query)
1145 nobody = nobody_uid()
1146 self.wfile.flush() # Always flush before forking
1147 pid = os.fork()
1148 if pid != 0:
1149 # Parent
1150 pid, sts = os.waitpid(pid, 0)
1151 # throw away additional data [see bug #427345]
1152 while select.select([self.rfile], [], [], 0)[0]:
1153 if not self.rfile.read(1):
1154 break
1155 if sts:
1156 self.log_error("CGI script exit status %#x", sts)
1157 return
1158 # Child
1159 try:
1160 try:
1161 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001162 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001163 pass
1164 os.dup2(self.rfile.fileno(), 0)
1165 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001166 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001167 except:
1168 self.server.handle_error(self.request, self.client_address)
1169 os._exit(127)
1170
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001171 else:
1172 # Non-Unix -- use subprocess
1173 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001174 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001175 if self.is_python(scriptfile):
1176 interp = sys.executable
1177 if interp.lower().endswith("w.exe"):
1178 # On Windows, use python.exe, not pythonw.exe
1179 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001180 cmdline = [interp, '-u'] + cmdline
1181 if '=' not in query:
1182 cmdline.append(query)
1183 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001184 try:
1185 nbytes = int(length)
1186 except (TypeError, ValueError):
1187 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001188 p = subprocess.Popen(cmdline,
1189 stdin=subprocess.PIPE,
1190 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001191 stderr=subprocess.PIPE,
1192 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001193 )
Georg Brandl24420152008-05-26 16:32:26 +00001194 if self.command.lower() == "post" and nbytes > 0:
1195 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001196 else:
1197 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001198 # throw away additional data [see bug #427345]
1199 while select.select([self.rfile._sock], [], [], 0)[0]:
1200 if not self.rfile._sock.recv(1):
1201 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001202 stdout, stderr = p.communicate(data)
1203 self.wfile.write(stdout)
1204 if stderr:
1205 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001206 p.stderr.close()
1207 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001208 status = p.returncode
1209 if status:
1210 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001211 else:
1212 self.log_message("CGI script exited OK")
1213
1214
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001215def test(HandlerClass=BaseHTTPRequestHandler,
1216 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001217 """Test the HTTP request handler class.
1218
Robert Collins9644f242015-08-17 12:18:35 +12001219 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001220
1221 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001222 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001223
1224 HandlerClass.protocol_version = protocol
Martin Panter0cab9c12016-04-13 00:36:52 +00001225 with ServerClass(server_address, HandlerClass) as httpd:
1226 sa = httpd.socket.getsockname()
Berker Peksag3a31cca2016-04-29 16:48:11 +03001227 serve_message = "Serving HTTP on {host} port {port} (http://{host}:{port}/) ..."
1228 print(serve_message.format(host=sa[0], port=sa[1]))
Martin Panter0cab9c12016-04-13 00:36:52 +00001229 try:
1230 httpd.serve_forever()
1231 except KeyboardInterrupt:
1232 print("\nKeyboard interrupt received, exiting.")
1233 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001234
1235if __name__ == '__main__':
Serhiy Storchaka7e4db2f2017-05-04 08:17:47 +03001236 import argparse
1237
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001238 parser = argparse.ArgumentParser()
1239 parser.add_argument('--cgi', action='store_true',
1240 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001241 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1242 help='Specify alternate bind address '
1243 '[default: all interfaces]')
Stéphane Wirtela17a2f52017-05-24 09:29:06 +02001244 parser.add_argument('--directory', '-d', default=os.getcwd(),
1245 help='Specify alternative directory '
1246 '[default:current directory]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001247 parser.add_argument('port', action='store',
1248 default=8000, type=int,
1249 nargs='?',
1250 help='Specify alternate port [default: 8000]')
1251 args = parser.parse_args()
1252 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001253 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001254 else:
Stéphane Wirtela17a2f52017-05-24 09:29:06 +02001255 handler_class = partial(SimpleHTTPRequestHandler,
1256 directory=args.directory)
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001257 test(HandlerClass=handler_class, port=args.port, bind=args.bind)