blob: ca2dd507392c8ec8de7eb94ede1efbe7f20cb02b [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__ = [
Julien Palard4f53e2a2018-05-30 02:24:17 +020086 "HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler",
Berker Peksag366c5702015-02-13 20:48:15 +020087 "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
Julien Palard4f53e2a2018-05-30 02:24:17 +0200143class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer):
Miss Islington (bot)f8d2c3c2018-03-23 13:31:20 -0700144 daemon_threads = True
145
146
Georg Brandl24420152008-05-26 16:32:26 +0000147class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
148
149 """HTTP request handler base class.
150
151 The following explanation of HTTP serves to guide you through the
152 code as well as to expose any misunderstandings I may have about
153 HTTP (so you don't need to read the code to figure out I'm wrong
154 :-).
155
156 HTTP (HyperText Transfer Protocol) is an extensible protocol on
157 top of a reliable stream transport (e.g. TCP/IP). The protocol
158 recognizes three parts to a request:
159
160 1. One line identifying the request type and path
161 2. An optional set of RFC-822-style headers
162 3. An optional data part
163
164 The headers and data are separated by a blank line.
165
166 The first line of the request has the form
167
168 <command> <path> <version>
169
170 where <command> is a (case-sensitive) keyword such as GET or POST,
171 <path> is a string containing path information for the request,
172 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
173 <path> is encoded using the URL encoding scheme (using %xx to signify
174 the ASCII character with hex code xx).
175
176 The specification specifies that lines are separated by CRLF but
177 for compatibility with the widest range of clients recommends
178 servers also handle LF. Similarly, whitespace in the request line
179 is treated sensibly (allowing multiple spaces between components
180 and allowing trailing whitespace).
181
182 Similarly, for output, lines ought to be separated by CRLF pairs
183 but most clients grok LF characters just fine.
184
185 If the first line of the request has the form
186
187 <command> <path>
188
189 (i.e. <version> is left out) then this is assumed to be an HTTP
190 0.9 request; this form has no optional headers and data part and
191 the reply consists of just the data.
192
193 The reply form of the HTTP 1.x protocol again has three parts:
194
195 1. One line giving the response code
196 2. An optional set of RFC-822-style headers
197 3. The data
198
199 Again, the headers and data are separated by a blank line.
200
201 The response code line has the form
202
203 <version> <responsecode> <responsestring>
204
205 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
206 <responsecode> is a 3-digit response code indicating success or
207 failure of the request, and <responsestring> is an optional
208 human-readable string explaining what the response code means.
209
210 This server parses the request and the headers, and then calls a
211 function specific to the request type (<command>). Specifically,
212 a request SPAM will be handled by a method do_SPAM(). If no
213 such method exists the server sends an error response to the
214 client. If it exists, it is called with no arguments:
215
216 do_SPAM()
217
218 Note that the request name is case sensitive (i.e. SPAM and spam
219 are different requests).
220
221 The various request details are stored in instance variables:
222
223 - client_address is the client IP address in the form (host,
224 port);
225
226 - command, path and version are the broken-down request line;
227
Barry Warsaw820c1202008-06-12 04:06:45 +0000228 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000229 class) containing the header information;
230
231 - rfile is a file object open for reading positioned at the
232 start of the optional input data part;
233
234 - wfile is a file object open for writing.
235
236 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
237
238 The first thing to be written must be the response line. Then
239 follow 0 or more header lines, then a blank line, and then the
240 actual data (if any). The meaning of the header lines depends on
241 the command executed by the server; in most cases, when data is
242 returned, there should be at least one header line of the form
243
244 Content-type: <type>/<subtype>
245
246 where <type> and <subtype> should be registered MIME types,
247 e.g. "text/html" or "text/plain".
248
249 """
250
251 # The Python system version, truncated to its first component.
252 sys_version = "Python/" + sys.version.split()[0]
253
254 # The server software version. You may want to override this.
255 # The format is multiple whitespace-separated strings,
256 # where each string is of the form name[/version].
257 server_version = "BaseHTTP/" + __version__
258
259 error_message_format = DEFAULT_ERROR_MESSAGE
260 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
261
262 # The default request version. This only affects responses up until
263 # the point where the request line is parsed, so it mainly decides what
264 # the client gets back when sending a malformed request line.
265 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
266 default_request_version = "HTTP/0.9"
267
268 def parse_request(self):
269 """Parse a request (internal).
270
271 The request should be stored in self.raw_requestline; the results
272 are in self.command, self.path, self.request_version and
273 self.headers.
274
Martin Pantere82338d2016-11-19 01:06:37 +0000275 Return True for success, False for failure; on failure, any relevant
276 error response has already been sent back.
Georg Brandl24420152008-05-26 16:32:26 +0000277
278 """
279 self.command = None # set in case of error on the first line
280 self.request_version = version = self.default_request_version
Benjamin Peterson70e28472015-02-17 21:11:10 -0500281 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000282 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800283 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000284 self.requestline = requestline
285 words = requestline.split()
Martin Pantere82338d2016-11-19 01:06:37 +0000286 if len(words) == 0:
287 return False
288
289 if len(words) >= 3: # Enough to determine protocol version
290 version = words[-1]
Georg Brandl24420152008-05-26 16:32:26 +0000291 try:
Martin Pantere82338d2016-11-19 01:06:37 +0000292 if not version.startswith('HTTP/'):
Martin Panter50badad2016-04-03 01:28:53 +0000293 raise ValueError
Georg Brandl24420152008-05-26 16:32:26 +0000294 base_version_number = version.split('/', 1)[1]
295 version_number = base_version_number.split(".")
296 # RFC 2145 section 3.1 says there can be only one "." and
297 # - major and minor numbers MUST be treated as
298 # separate integers;
299 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
300 # turn is lower than HTTP/12.3;
301 # - Leading zeros MUST be ignored by recipients.
302 if len(version_number) != 2:
303 raise ValueError
304 version_number = int(version_number[0]), int(version_number[1])
305 except (ValueError, IndexError):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200306 self.send_error(
307 HTTPStatus.BAD_REQUEST,
308 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000309 return False
310 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
Benjamin Peterson70e28472015-02-17 21:11:10 -0500311 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000312 if version_number >= (2, 0):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200313 self.send_error(
314 HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
Martin Panter50badad2016-04-03 01:28:53 +0000315 "Invalid HTTP version (%s)" % base_version_number)
Georg Brandl24420152008-05-26 16:32:26 +0000316 return False
Martin Pantere82338d2016-11-19 01:06:37 +0000317 self.request_version = version
318
319 if not 2 <= len(words) <= 3:
320 self.send_error(
321 HTTPStatus.BAD_REQUEST,
322 "Bad request syntax (%r)" % requestline)
323 return False
324 command, path = words[:2]
325 if len(words) == 2:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500326 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000327 if command != 'GET':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200328 self.send_error(
329 HTTPStatus.BAD_REQUEST,
330 "Bad HTTP/0.9 request type (%r)" % command)
Georg Brandl24420152008-05-26 16:32:26 +0000331 return False
Martin Pantere82338d2016-11-19 01:06:37 +0000332 self.command, self.path = command, path
Georg Brandl24420152008-05-26 16:32:26 +0000333
334 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000335 try:
336 self.headers = http.client.parse_headers(self.rfile,
337 _class=self.MessageClass)
Martin Panter50badad2016-04-03 01:28:53 +0000338 except http.client.LineTooLong as err:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200339 self.send_error(
Martin Panter50badad2016-04-03 01:28:53 +0000340 HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
341 "Line too long",
342 str(err))
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000343 return False
Martin Panteracc03192016-04-03 00:45:46 +0000344 except http.client.HTTPException as err:
345 self.send_error(
346 HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
347 "Too many headers",
348 str(err)
349 )
350 return False
Georg Brandl24420152008-05-26 16:32:26 +0000351
352 conntype = self.headers.get('Connection', "")
353 if conntype.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500354 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000355 elif (conntype.lower() == 'keep-alive' and
356 self.protocol_version >= "HTTP/1.1"):
Benjamin Peterson70e28472015-02-17 21:11:10 -0500357 self.close_connection = False
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000358 # Examine the headers and look for an Expect directive
359 expect = self.headers.get('Expect', "")
360 if (expect.lower() == "100-continue" and
361 self.protocol_version >= "HTTP/1.1" and
362 self.request_version >= "HTTP/1.1"):
363 if not self.handle_expect_100():
364 return False
365 return True
366
367 def handle_expect_100(self):
368 """Decide what to do with an "Expect: 100-continue" header.
369
370 If the client is expecting a 100 Continue response, we must
371 respond with either a 100 Continue or a final response before
372 waiting for the request body. The default is to always respond
373 with a 100 Continue. You can behave differently (for example,
374 reject unauthorized requests) by overriding this method.
375
376 This method should either return True (possibly after sending
377 a 100 Continue response) or send an error response and return
378 False.
379
380 """
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200381 self.send_response_only(HTTPStatus.CONTINUE)
Benjamin Peterson04424232014-01-18 21:50:18 -0500382 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000383 return True
384
385 def handle_one_request(self):
386 """Handle a single HTTP request.
387
388 You normally don't need to override this method; see the class
389 __doc__ string for information on how to handle specific HTTP
390 commands such as GET and POST.
391
392 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000393 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000394 self.raw_requestline = self.rfile.readline(65537)
395 if len(self.raw_requestline) > 65536:
396 self.requestline = ''
397 self.request_version = ''
398 self.command = ''
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200399 self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
Antoine Pitrouc4924372010-12-16 16:48:36 +0000400 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000401 if not self.raw_requestline:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500402 self.close_connection = True
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000403 return
404 if not self.parse_request():
405 # An error code has been sent, just exit
406 return
407 mname = 'do_' + self.command
408 if not hasattr(self, mname):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200409 self.send_error(
410 HTTPStatus.NOT_IMPLEMENTED,
411 "Unsupported method (%r)" % self.command)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000412 return
413 method = getattr(self, mname)
414 method()
415 self.wfile.flush() #actually send the response if not already done.
416 except socket.timeout as e:
417 #a read or a write timed out. Discard this connection
418 self.log_error("Request timed out: %r", e)
Benjamin Peterson70e28472015-02-17 21:11:10 -0500419 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000420 return
Georg Brandl24420152008-05-26 16:32:26 +0000421
422 def handle(self):
423 """Handle multiple requests if necessary."""
Benjamin Peterson70e28472015-02-17 21:11:10 -0500424 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000425
426 self.handle_one_request()
427 while not self.close_connection:
428 self.handle_one_request()
429
Senthil Kumaran26886442013-03-15 07:53:21 -0700430 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000431 """Send and log an error reply.
432
Senthil Kumaran26886442013-03-15 07:53:21 -0700433 Arguments are
434 * code: an HTTP error code
435 3 digits
436 * message: a simple optional 1 line reason phrase.
437 *( HTAB / SP / VCHAR / %x80-FF )
438 defaults to short entry matching the response code
439 * explain: a detailed message defaults to the long entry
440 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000441
442 This sends an error response (so it must be called before any
443 output has been generated), logs the error, and finally sends
444 a piece of HTML explaining the error to the user.
445
446 """
447
448 try:
449 shortmsg, longmsg = self.responses[code]
450 except KeyError:
451 shortmsg, longmsg = '???', '???'
452 if message is None:
453 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700454 if explain is None:
455 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000456 self.log_error("code %d, message %s", code, message)
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800457 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000458 self.send_header('Connection', 'close')
Martin Pantere42e1292016-06-08 08:29:13 +0000459
460 # Message body is omitted for cases described in:
461 # - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
462 # - RFC7231: 6.3.6. 205(Reset Content)
463 body = None
464 if (code >= 200 and
465 code not in (HTTPStatus.NO_CONTENT,
466 HTTPStatus.RESET_CONTENT,
467 HTTPStatus.NOT_MODIFIED)):
468 # HTML encode to prevent Cross Site Scripting attacks
469 # (see bug #1100201)
470 content = (self.error_message_format % {
471 'code': code,
Martin Panter40de69a2016-06-08 09:45:58 +0000472 'message': html.escape(message, quote=False),
473 'explain': html.escape(explain, quote=False)
Martin Pantere42e1292016-06-08 08:29:13 +0000474 })
475 body = content.encode('UTF-8', 'replace')
476 self.send_header("Content-Type", self.error_content_type)
Miss Islington (bot)53d1e9f2018-06-18 14:37:56 -0700477 self.send_header('Content-Length', str(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000478 self.end_headers()
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200479
Martin Pantere42e1292016-06-08 08:29:13 +0000480 if self.command != 'HEAD' and body:
Senthil Kumaran52d27202012-10-10 23:16:21 -0700481 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000482
483 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800484 """Add the response header to the headers buffer and log the
485 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000486
487 Also send two standard headers with the server software
488 version and the current date.
489
490 """
491 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000492 self.send_response_only(code, message)
493 self.send_header('Server', self.version_string())
494 self.send_header('Date', self.date_time_string())
495
496 def send_response_only(self, code, message=None):
497 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000498 if self.request_version != 'HTTP/0.9':
Martin Panter50badad2016-04-03 01:28:53 +0000499 if message is None:
500 if code in self.responses:
501 message = self.responses[code][0]
502 else:
503 message = ''
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800504 if not hasattr(self, '_headers_buffer'):
505 self._headers_buffer = []
506 self._headers_buffer.append(("%s %d %s\r\n" %
507 (self.protocol_version, code, message)).encode(
508 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000509
510 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800511 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000512 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000513 if not hasattr(self, '_headers_buffer'):
514 self._headers_buffer = []
515 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000516 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000517
518 if keyword.lower() == 'connection':
519 if value.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500520 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000521 elif value.lower() == 'keep-alive':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500522 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000523
524 def end_headers(self):
525 """Send the blank line ending the MIME headers."""
526 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000527 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800528 self.flush_headers()
529
530 def flush_headers(self):
531 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000532 self.wfile.write(b"".join(self._headers_buffer))
533 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000534
535 def log_request(self, code='-', size='-'):
536 """Log an accepted request.
537
538 This is called by send_response().
539
540 """
Serhiy Storchakac0a23e62015-03-07 11:51:37 +0200541 if isinstance(code, HTTPStatus):
542 code = code.value
Georg Brandl24420152008-05-26 16:32:26 +0000543 self.log_message('"%s" %s %s',
544 self.requestline, str(code), str(size))
545
546 def log_error(self, format, *args):
547 """Log an error.
548
549 This is called when a request cannot be fulfilled. By
550 default it passes the message on to log_message().
551
552 Arguments are the same as for log_message().
553
554 XXX This should go to the separate error log.
555
556 """
557
558 self.log_message(format, *args)
559
560 def log_message(self, format, *args):
561 """Log an arbitrary message.
562
563 This is used by all other logging functions. Override
564 it if you have specific logging wishes.
565
566 The first argument, FORMAT, is a format string for the
567 message to be logged. If the format string contains
568 any % escapes requiring parameters, they should be
569 specified as subsequent arguments (it's just like
570 printf!).
571
Senthil Kumarandb727b42012-04-29 13:41:03 +0800572 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000573 every message.
574
575 """
576
577 sys.stderr.write("%s - - [%s] %s\n" %
578 (self.address_string(),
579 self.log_date_time_string(),
580 format%args))
581
582 def version_string(self):
583 """Return the server software version string."""
584 return self.server_version + ' ' + self.sys_version
585
586 def date_time_string(self, timestamp=None):
587 """Return the current date and time formatted for a message header."""
588 if timestamp is None:
589 timestamp = time.time()
Berker Peksag04bc5b92016-03-14 06:06:03 +0200590 return email.utils.formatdate(timestamp, usegmt=True)
Georg Brandl24420152008-05-26 16:32:26 +0000591
592 def log_date_time_string(self):
593 """Return the current time formatted for logging."""
594 now = time.time()
595 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
596 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
597 day, self.monthname[month], year, hh, mm, ss)
598 return s
599
600 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
601
602 monthname = [None,
603 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
604 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
605
606 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800607 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000608
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800609 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000610
611 # Essentially static class variables
612
613 # The version of the HTTP protocol we support.
614 # Set this to HTTP/1.1 to enable automatic keepalive
615 protocol_version = "HTTP/1.0"
616
Barry Warsaw820c1202008-06-12 04:06:45 +0000617 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000618 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000619
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200620 # hack to maintain backwards compatibility
Georg Brandl24420152008-05-26 16:32:26 +0000621 responses = {
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200622 v: (v.phrase, v.description)
623 for v in HTTPStatus.__members__.values()
624 }
Georg Brandl24420152008-05-26 16:32:26 +0000625
626
627class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
628
629 """Simple HTTP request handler with GET and HEAD commands.
630
631 This serves files from the current directory and any of its
632 subdirectories. The MIME type for files is determined by
633 calling the .guess_type() method.
634
635 The GET and HEAD requests are identical except that the HEAD
636 request omits the actual contents of the file.
637
638 """
639
640 server_version = "SimpleHTTP/" + __version__
641
Stéphane Wirtela17a2f52017-05-24 09:29:06 +0200642 def __init__(self, *args, directory=None, **kwargs):
643 if directory is None:
644 directory = os.getcwd()
645 self.directory = directory
646 super().__init__(*args, **kwargs)
647
Georg Brandl24420152008-05-26 16:32:26 +0000648 def do_GET(self):
649 """Serve a GET request."""
650 f = self.send_head()
651 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200652 try:
653 self.copyfile(f, self.wfile)
654 finally:
655 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000656
657 def do_HEAD(self):
658 """Serve a HEAD request."""
659 f = self.send_head()
660 if f:
661 f.close()
662
663 def send_head(self):
664 """Common code for GET and HEAD commands.
665
666 This sends the response code and MIME headers.
667
668 Return value is either a file object (which has to be copied
669 to the outputfile by the caller unless the command was HEAD,
670 and must be closed by the caller under all circumstances), or
671 None, in which case the caller has nothing further to do.
672
673 """
674 path = self.translate_path(self.path)
675 f = None
676 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600677 parts = urllib.parse.urlsplit(self.path)
678 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000679 # redirect browser - doing basically what apache does
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200680 self.send_response(HTTPStatus.MOVED_PERMANENTLY)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600681 new_parts = (parts[0], parts[1], parts[2] + '/',
682 parts[3], parts[4])
683 new_url = urllib.parse.urlunsplit(new_parts)
684 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000685 self.end_headers()
686 return None
687 for index in "index.html", "index.htm":
688 index = os.path.join(path, index)
689 if os.path.exists(index):
690 path = index
691 break
692 else:
693 return self.list_directory(path)
694 ctype = self.guess_type(path)
695 try:
696 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200697 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200698 self.send_error(HTTPStatus.NOT_FOUND, "File not found")
Georg Brandl24420152008-05-26 16:32:26 +0000699 return None
Pierre Quentel351adda2017-04-02 12:26:12 +0200700
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200701 try:
Pierre Quentel351adda2017-04-02 12:26:12 +0200702 fs = os.fstat(f.fileno())
703 # Use browser cache if possible
704 if ("If-Modified-Since" in self.headers
705 and "If-None-Match" not in self.headers):
706 # compare If-Modified-Since and time of last file modification
707 try:
708 ims = email.utils.parsedate_to_datetime(
709 self.headers["If-Modified-Since"])
710 except (TypeError, IndexError, OverflowError, ValueError):
711 # ignore ill-formed values
712 pass
713 else:
714 if ims.tzinfo is None:
715 # obsolete format with no timezone, cf.
716 # https://tools.ietf.org/html/rfc7231#section-7.1.1.1
717 ims = ims.replace(tzinfo=datetime.timezone.utc)
718 if ims.tzinfo is datetime.timezone.utc:
719 # compare to UTC datetime of last modification
720 last_modif = datetime.datetime.fromtimestamp(
721 fs.st_mtime, datetime.timezone.utc)
722 # remove microseconds, like in If-Modified-Since
723 last_modif = last_modif.replace(microsecond=0)
Serhiy Storchaka13ad3b72017-09-14 09:38:36 +0300724
Pierre Quentel351adda2017-04-02 12:26:12 +0200725 if last_modif <= ims:
726 self.send_response(HTTPStatus.NOT_MODIFIED)
727 self.end_headers()
728 f.close()
729 return None
730
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200731 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200732 self.send_header("Content-type", ctype)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200733 self.send_header("Content-Length", str(fs[6]))
Serhiy Storchaka13ad3b72017-09-14 09:38:36 +0300734 self.send_header("Last-Modified",
Pierre Quentel351adda2017-04-02 12:26:12 +0200735 self.date_time_string(fs.st_mtime))
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200736 self.end_headers()
737 return f
738 except:
739 f.close()
740 raise
Georg Brandl24420152008-05-26 16:32:26 +0000741
742 def list_directory(self, path):
743 """Helper to produce a directory listing (absent index.html).
744
745 Return value is either a file object, or None (indicating an
746 error). In either case, the headers are sent, making the
747 interface the same as for send_head().
748
749 """
750 try:
751 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200752 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200753 self.send_error(
754 HTTPStatus.NOT_FOUND,
755 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000756 return None
757 list.sort(key=lambda a: a.lower())
758 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300759 try:
760 displaypath = urllib.parse.unquote(self.path,
761 errors='surrogatepass')
762 except UnicodeDecodeError:
763 displaypath = urllib.parse.unquote(path)
Martin Panterda3bb382016-04-11 00:40:08 +0000764 displaypath = html.escape(displaypath, quote=False)
Ezio Melottica897e92011-11-02 19:33:29 +0200765 enc = sys.getfilesystemencoding()
766 title = 'Directory listing for %s' % displaypath
767 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
768 '"http://www.w3.org/TR/html4/strict.dtd">')
769 r.append('<html>\n<head>')
770 r.append('<meta http-equiv="Content-Type" '
771 'content="text/html; charset=%s">' % enc)
772 r.append('<title>%s</title>\n</head>' % title)
773 r.append('<body>\n<h1>%s</h1>' % title)
774 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000775 for name in list:
776 fullname = os.path.join(path, name)
777 displayname = linkname = name
778 # Append / for directories or @ for symbolic links
779 if os.path.isdir(fullname):
780 displayname = name + "/"
781 linkname = name + "/"
782 if os.path.islink(fullname):
783 displayname = name + "@"
784 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200785 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300786 % (urllib.parse.quote(linkname,
787 errors='surrogatepass'),
Martin Panterda3bb382016-04-11 00:40:08 +0000788 html.escape(displayname, quote=False)))
Ezio Melottica897e92011-11-02 19:33:29 +0200789 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300790 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000791 f = io.BytesIO()
792 f.write(encoded)
793 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200794 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000795 self.send_header("Content-type", "text/html; charset=%s" % enc)
796 self.send_header("Content-Length", str(len(encoded)))
797 self.end_headers()
798 return f
799
800 def translate_path(self, path):
801 """Translate a /-separated PATH to the local filename syntax.
802
803 Components that mean special things to the local file system
804 (e.g. drive or directory names) are ignored. (XXX They should
805 probably be diagnosed.)
806
807 """
808 # abandon query parameters
809 path = path.split('?',1)[0]
810 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700811 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700812 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300813 try:
814 path = urllib.parse.unquote(path, errors='surrogatepass')
815 except UnicodeDecodeError:
816 path = urllib.parse.unquote(path)
817 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000818 words = path.split('/')
819 words = filter(None, words)
Stéphane Wirtela17a2f52017-05-24 09:29:06 +0200820 path = self.directory
Georg Brandl24420152008-05-26 16:32:26 +0000821 for word in words:
Martin Panterd274b3f2016-04-18 03:45:18 +0000822 if os.path.dirname(word) or word in (os.curdir, os.pardir):
823 # Ignore components that are not a simple file/directory name
824 continue
Georg Brandl24420152008-05-26 16:32:26 +0000825 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700826 if trailing_slash:
827 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000828 return path
829
830 def copyfile(self, source, outputfile):
831 """Copy all data between two file objects.
832
833 The SOURCE argument is a file object open for reading
834 (or anything with a read() method) and the DESTINATION
835 argument is a file object open for writing (or
836 anything with a write() method).
837
838 The only reason for overriding this would be to change
839 the block size or perhaps to replace newlines by CRLF
840 -- note however that this the default server uses this
841 to copy binary data as well.
842
843 """
844 shutil.copyfileobj(source, outputfile)
845
846 def guess_type(self, path):
847 """Guess the type of a file.
848
849 Argument is a PATH (a filename).
850
851 Return value is a string of the form type/subtype,
852 usable for a MIME Content-type header.
853
854 The default implementation looks the file's extension
855 up in the table self.extensions_map, using application/octet-stream
856 as a default; however it would be permissible (if
857 slow) to look inside the data to make a better guess.
858
859 """
860
861 base, ext = posixpath.splitext(path)
862 if ext in self.extensions_map:
863 return self.extensions_map[ext]
864 ext = ext.lower()
865 if ext in self.extensions_map:
866 return self.extensions_map[ext]
867 else:
868 return self.extensions_map['']
869
870 if not mimetypes.inited:
871 mimetypes.init() # try to read system mime.types
872 extensions_map = mimetypes.types_map.copy()
873 extensions_map.update({
874 '': 'application/octet-stream', # Default
875 '.py': 'text/plain',
876 '.c': 'text/plain',
877 '.h': 'text/plain',
878 })
879
880
881# Utilities for CGIHTTPRequestHandler
882
Senthil Kumarand70846b2012-04-12 02:34:32 +0800883def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000884 """
885 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000886 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000887
888 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800889 The utility of this function is limited to is_cgi method and helps
890 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000891
Martin Pantercb29e8c2015-10-03 05:55:46 +0000892 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000893
894 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800895
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000896 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000897 # Query component should not be involved.
898 path, _, query = path.partition('?')
899 path = urllib.parse.unquote(path)
900
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000901 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
902 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800903 path_parts = path.split('/')
904 head_parts = []
905 for part in path_parts[:-1]:
906 if part == '..':
907 head_parts.pop() # IndexError if more '..' than prior parts
908 elif part and part != '.':
909 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000910 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800911 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800912 if tail_part:
913 if tail_part == '..':
914 head_parts.pop()
915 tail_part = ''
916 elif tail_part == '.':
917 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000918 else:
919 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800920
Martin Pantercb29e8c2015-10-03 05:55:46 +0000921 if query:
922 tail_part = '?'.join((tail_part, query))
923
Senthil Kumarand70846b2012-04-12 02:34:32 +0800924 splitpath = ('/' + '/'.join(head_parts), tail_part)
925 collapsed_path = "/".join(splitpath)
926
927 return collapsed_path
928
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000929
930
Georg Brandl24420152008-05-26 16:32:26 +0000931nobody = None
932
933def nobody_uid():
934 """Internal routine to get nobody's uid"""
935 global nobody
936 if nobody:
937 return nobody
938 try:
939 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400940 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000941 return -1
942 try:
943 nobody = pwd.getpwnam('nobody')[2]
944 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000945 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000946 return nobody
947
948
949def executable(path):
950 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200951 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000952
953
954class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
955
956 """Complete HTTP server with GET, HEAD and POST commands.
957
958 GET and HEAD also support running CGI scripts.
959
960 The POST command is *only* implemented for CGI scripts.
961
962 """
963
964 # Determine platform specifics
965 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000966
967 # Make rfile unbuffered -- we need to read one line and then pass
968 # the rest to a subprocess, so we can't use buffered input.
969 rbufsize = 0
970
971 def do_POST(self):
972 """Serve a POST request.
973
974 This is only implemented for CGI scripts.
975
976 """
977
978 if self.is_cgi():
979 self.run_cgi()
980 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200981 self.send_error(
982 HTTPStatus.NOT_IMPLEMENTED,
983 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000984
985 def send_head(self):
986 """Version of send_head that support CGI scripts"""
987 if self.is_cgi():
988 return self.run_cgi()
989 else:
990 return SimpleHTTPRequestHandler.send_head(self)
991
992 def is_cgi(self):
993 """Test whether self.path corresponds to a CGI script.
994
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000995 Returns True and updates the cgi_info attribute to the tuple
996 (dir, rest) if self.path requires running a CGI script.
997 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000998
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000999 If any exception is raised, the caller should assume that
1000 self.path was rejected as invalid and act accordingly.
1001
Benjamin Petersonad71f0f2009-04-11 20:12:10 +00001002 The default implementation tests whether the normalized url
1003 path begins with one of the strings in self.cgi_directories
1004 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +00001005
1006 """
Martin Pantercb29e8c2015-10-03 05:55:46 +00001007 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +08001008 dir_sep = collapsed_path.find('/', 1)
1009 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +08001010 if head in self.cgi_directories:
1011 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +00001012 return True
Georg Brandl24420152008-05-26 16:32:26 +00001013 return False
1014
Senthil Kumarand70846b2012-04-12 02:34:32 +08001015
Georg Brandl24420152008-05-26 16:32:26 +00001016 cgi_directories = ['/cgi-bin', '/htbin']
1017
1018 def is_executable(self, path):
1019 """Test whether argument path is an executable file."""
1020 return executable(path)
1021
1022 def is_python(self, path):
1023 """Test whether argument path is a Python script."""
1024 head, tail = os.path.splitext(path)
1025 return tail.lower() in (".py", ".pyw")
1026
1027 def run_cgi(self):
1028 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +00001029 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -07001030 path = dir + '/' + rest
1031 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001032 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -07001033 nextdir = path[:i]
1034 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +00001035
1036 scriptdir = self.translate_path(nextdir)
1037 if os.path.isdir(scriptdir):
1038 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -07001039 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001040 else:
1041 break
1042
1043 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +00001044 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +00001045
1046 # dissect the part after the directory name into a script name &
1047 # a possible additional path, to be stored in PATH_INFO.
1048 i = rest.find('/')
1049 if i >= 0:
1050 script, rest = rest[:i], rest[i:]
1051 else:
1052 script, rest = rest, ''
1053
1054 scriptname = dir + '/' + script
1055 scriptfile = self.translate_path(scriptname)
1056 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001057 self.send_error(
1058 HTTPStatus.NOT_FOUND,
1059 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001060 return
1061 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001062 self.send_error(
1063 HTTPStatus.FORBIDDEN,
1064 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001065 return
1066 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001067 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001068 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001069 self.send_error(
1070 HTTPStatus.FORBIDDEN,
1071 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001072 return
1073
1074 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1075 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001076 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001077 env['SERVER_SOFTWARE'] = self.version_string()
1078 env['SERVER_NAME'] = self.server.server_name
1079 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1080 env['SERVER_PROTOCOL'] = self.protocol_version
1081 env['SERVER_PORT'] = str(self.server.server_port)
1082 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001083 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001084 env['PATH_INFO'] = uqrest
1085 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1086 env['SCRIPT_NAME'] = scriptname
1087 if query:
1088 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001089 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001090 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001091 if authorization:
1092 authorization = authorization.split()
1093 if len(authorization) == 2:
1094 import base64, binascii
1095 env['AUTH_TYPE'] = authorization[0]
1096 if authorization[0].lower() == "basic":
1097 try:
1098 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001099 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001100 decode('ascii')
1101 except (binascii.Error, UnicodeError):
1102 pass
1103 else:
1104 authorization = authorization.split(':')
1105 if len(authorization) == 2:
1106 env['REMOTE_USER'] = authorization[0]
1107 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001108 if self.headers.get('content-type') is None:
1109 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001110 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001111 env['CONTENT_TYPE'] = self.headers['content-type']
1112 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001113 if length:
1114 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001115 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001116 if referer:
1117 env['HTTP_REFERER'] = referer
1118 accept = []
1119 for line in self.headers.getallmatchingheaders('accept'):
1120 if line[:1] in "\t\n\r ":
1121 accept.append(line.strip())
1122 else:
1123 accept = accept + line[7:].split(',')
1124 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001125 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001126 if ua:
1127 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001128 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001129 cookie_str = ', '.join(co)
1130 if cookie_str:
1131 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001132 # XXX Other HTTP_* headers
1133 # Since we're setting the env in the parent, provide empty
1134 # values to override previously set values
1135 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1136 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1137 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001138
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001139 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001140 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001141
1142 decoded_query = query.replace('+', ' ')
1143
1144 if self.have_fork:
1145 # Unix -- fork as we should
1146 args = [script]
1147 if '=' not in decoded_query:
1148 args.append(decoded_query)
1149 nobody = nobody_uid()
1150 self.wfile.flush() # Always flush before forking
1151 pid = os.fork()
1152 if pid != 0:
1153 # Parent
1154 pid, sts = os.waitpid(pid, 0)
1155 # throw away additional data [see bug #427345]
1156 while select.select([self.rfile], [], [], 0)[0]:
1157 if not self.rfile.read(1):
1158 break
1159 if sts:
1160 self.log_error("CGI script exit status %#x", sts)
1161 return
1162 # Child
1163 try:
1164 try:
1165 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001166 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001167 pass
1168 os.dup2(self.rfile.fileno(), 0)
1169 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001170 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001171 except:
1172 self.server.handle_error(self.request, self.client_address)
1173 os._exit(127)
1174
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001175 else:
1176 # Non-Unix -- use subprocess
1177 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001178 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001179 if self.is_python(scriptfile):
1180 interp = sys.executable
1181 if interp.lower().endswith("w.exe"):
1182 # On Windows, use python.exe, not pythonw.exe
1183 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001184 cmdline = [interp, '-u'] + cmdline
1185 if '=' not in query:
1186 cmdline.append(query)
1187 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001188 try:
1189 nbytes = int(length)
1190 except (TypeError, ValueError):
1191 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001192 p = subprocess.Popen(cmdline,
1193 stdin=subprocess.PIPE,
1194 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001195 stderr=subprocess.PIPE,
1196 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001197 )
Georg Brandl24420152008-05-26 16:32:26 +00001198 if self.command.lower() == "post" and nbytes > 0:
1199 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001200 else:
1201 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001202 # throw away additional data [see bug #427345]
1203 while select.select([self.rfile._sock], [], [], 0)[0]:
1204 if not self.rfile._sock.recv(1):
1205 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001206 stdout, stderr = p.communicate(data)
1207 self.wfile.write(stdout)
1208 if stderr:
1209 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001210 p.stderr.close()
1211 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001212 status = p.returncode
1213 if status:
1214 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001215 else:
1216 self.log_message("CGI script exited OK")
1217
1218
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001219def test(HandlerClass=BaseHTTPRequestHandler,
Julien Palard4f53e2a2018-05-30 02:24:17 +02001220 ServerClass=ThreadingHTTPServer,
Miss Islington (bot)f8d2c3c2018-03-23 13:31:20 -07001221 protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001222 """Test the HTTP request handler class.
1223
Robert Collins9644f242015-08-17 12:18:35 +12001224 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001225
1226 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001227 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001228
1229 HandlerClass.protocol_version = protocol
Martin Panter0cab9c12016-04-13 00:36:52 +00001230 with ServerClass(server_address, HandlerClass) as httpd:
1231 sa = httpd.socket.getsockname()
Berker Peksag3a31cca2016-04-29 16:48:11 +03001232 serve_message = "Serving HTTP on {host} port {port} (http://{host}:{port}/) ..."
1233 print(serve_message.format(host=sa[0], port=sa[1]))
Martin Panter0cab9c12016-04-13 00:36:52 +00001234 try:
1235 httpd.serve_forever()
1236 except KeyboardInterrupt:
1237 print("\nKeyboard interrupt received, exiting.")
1238 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001239
1240if __name__ == '__main__':
Serhiy Storchaka7e4db2f2017-05-04 08:17:47 +03001241 import argparse
1242
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001243 parser = argparse.ArgumentParser()
1244 parser.add_argument('--cgi', action='store_true',
1245 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001246 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1247 help='Specify alternate bind address '
1248 '[default: all interfaces]')
Stéphane Wirtela17a2f52017-05-24 09:29:06 +02001249 parser.add_argument('--directory', '-d', default=os.getcwd(),
1250 help='Specify alternative directory '
1251 '[default:current directory]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001252 parser.add_argument('port', action='store',
1253 default=8000, type=int,
1254 nargs='?',
1255 help='Specify alternate port [default: 8000]')
1256 args = parser.parse_args()
1257 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001258 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001259 else:
Stéphane Wirtela17a2f52017-05-24 09:29:06 +02001260 handler_class = partial(SimpleHTTPRequestHandler,
1261 directory=args.directory)
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001262 test(HandlerClass=handler_class, port=args.port, bind=args.bind)