blob: 5e91826d15fb8dda2b5f6d831db0a6b3be97ff71 [file] [log] [blame]
Georg Brandl24420152008-05-26 16:32:26 +00001"""HTTP server classes.
2
3Note: BaseHTTPRequestHandler doesn't implement any HTTP request; see
4SimpleHTTPRequestHandler for simple implementations of GET, HEAD and POST,
5and CGIHTTPRequestHandler for CGI scripts.
6
7It does, however, optionally implement HTTP/1.1 persistent connections,
8as of version 0.3.
9
10Notes on CGIHTTPRequestHandler
11------------------------------
12
13This class implements GET and POST requests to cgi-bin scripts.
14
15If the os.fork() function is not present (e.g. on Windows),
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +000016subprocess.Popen() is used as a fallback, with slightly altered semantics.
Georg Brandl24420152008-05-26 16:32:26 +000017
18In all cases, the implementation is intentionally naive -- all
19requests are executed synchronously.
20
21SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
22-- it may execute arbitrary Python code or external programs.
23
24Note that status code 200 is sent prior to execution of a CGI script, so
25scripts cannot send other status codes such as 302 (redirect).
26
27XXX To do:
28
29- log requests even later (to capture byte count)
30- log user-agent header and other interesting goodies
31- send error log to separate file
32"""
33
34
35# See also:
36#
37# HTTP Working Group T. Berners-Lee
38# INTERNET-DRAFT R. T. Fielding
39# <draft-ietf-http-v10-spec-00.txt> H. Frystyk Nielsen
40# Expires September 8, 1995 March 8, 1995
41#
42# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt
43#
44# and
45#
46# Network Working Group R. Fielding
47# Request for Comments: 2616 et al
48# Obsoletes: 2068 June 1999
49# Category: Standards Track
50#
51# URL: http://www.faqs.org/rfcs/rfc2616.html
52
53# Log files
54# ---------
55#
56# Here's a quote from the NCSA httpd docs about log file format.
57#
58# | The logfile format is as follows. Each line consists of:
59# |
60# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb
61# |
62# | host: Either the DNS name or the IP number of the remote client
63# | rfc931: Any information returned by identd for this person,
64# | - otherwise.
65# | authuser: If user sent a userid for authentication, the user name,
66# | - otherwise.
67# | DD: Day
68# | Mon: Month (calendar name)
69# | YYYY: Year
70# | hh: hour (24-hour format, the machine's timezone)
71# | mm: minutes
72# | ss: seconds
73# | request: The first line of the HTTP request as sent by the client.
74# | ddd: the status code returned by the server, - if not available.
75# | bbbb: the total number of bytes sent,
76# | *not including the HTTP/1.0 header*, - if not available
77# |
78# | You can determine the name of the file accessed through request.
79#
80# (Actually, the latter is only true if you know the server configuration
81# at the time the request was made!)
82
83__version__ = "0.6"
84
Berker Peksag366c5702015-02-13 20:48:15 +020085__all__ = [
86 "HTTPServer", "BaseHTTPRequestHandler",
87 "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler",
88]
Georg Brandl24420152008-05-26 16:32:26 +000089
Berker Peksag04bc5b92016-03-14 06:06:03 +020090import email.utils
Georg Brandl1f7fffb2010-10-15 15:57:45 +000091import html
Jeremy Hylton914ab452009-03-27 17:16:06 +000092import http.client
93import io
94import mimetypes
95import os
96import posixpath
97import select
98import shutil
99import socket # For gethostbyaddr()
100import socketserver
101import sys
102import time
103import urllib.parse
Senthil Kumaran42713722010-10-03 17:55:45 +0000104import copy
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800105import argparse
106
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200107from http import HTTPStatus
108
Georg Brandl24420152008-05-26 16:32:26 +0000109
110# Default error message template
111DEFAULT_ERROR_MESSAGE = """\
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800112<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
113 "http://www.w3.org/TR/html4/strict.dtd">
Ezio Melottica897e92011-11-02 19:33:29 +0200114<html>
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800115 <head>
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800116 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800117 <title>Error response</title>
118 </head>
119 <body>
120 <h1>Error response</h1>
121 <p>Error code: %(code)d</p>
122 <p>Message: %(message)s.</p>
123 <p>Error code explanation: %(code)s - %(explain)s.</p>
124 </body>
125</html>
Georg Brandl24420152008-05-26 16:32:26 +0000126"""
127
128DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8"
129
130def _quote_html(html):
131 return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
132
133class HTTPServer(socketserver.TCPServer):
134
135 allow_reuse_address = 1 # Seems to make sense in testing environment
136
137 def server_bind(self):
138 """Override server_bind to store the server name."""
139 socketserver.TCPServer.server_bind(self)
140 host, port = self.socket.getsockname()[:2]
141 self.server_name = socket.getfqdn(host)
142 self.server_port = port
143
144
145class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
146
147 """HTTP request handler base class.
148
149 The following explanation of HTTP serves to guide you through the
150 code as well as to expose any misunderstandings I may have about
151 HTTP (so you don't need to read the code to figure out I'm wrong
152 :-).
153
154 HTTP (HyperText Transfer Protocol) is an extensible protocol on
155 top of a reliable stream transport (e.g. TCP/IP). The protocol
156 recognizes three parts to a request:
157
158 1. One line identifying the request type and path
159 2. An optional set of RFC-822-style headers
160 3. An optional data part
161
162 The headers and data are separated by a blank line.
163
164 The first line of the request has the form
165
166 <command> <path> <version>
167
168 where <command> is a (case-sensitive) keyword such as GET or POST,
169 <path> is a string containing path information for the request,
170 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
171 <path> is encoded using the URL encoding scheme (using %xx to signify
172 the ASCII character with hex code xx).
173
174 The specification specifies that lines are separated by CRLF but
175 for compatibility with the widest range of clients recommends
176 servers also handle LF. Similarly, whitespace in the request line
177 is treated sensibly (allowing multiple spaces between components
178 and allowing trailing whitespace).
179
180 Similarly, for output, lines ought to be separated by CRLF pairs
181 but most clients grok LF characters just fine.
182
183 If the first line of the request has the form
184
185 <command> <path>
186
187 (i.e. <version> is left out) then this is assumed to be an HTTP
188 0.9 request; this form has no optional headers and data part and
189 the reply consists of just the data.
190
191 The reply form of the HTTP 1.x protocol again has three parts:
192
193 1. One line giving the response code
194 2. An optional set of RFC-822-style headers
195 3. The data
196
197 Again, the headers and data are separated by a blank line.
198
199 The response code line has the form
200
201 <version> <responsecode> <responsestring>
202
203 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
204 <responsecode> is a 3-digit response code indicating success or
205 failure of the request, and <responsestring> is an optional
206 human-readable string explaining what the response code means.
207
208 This server parses the request and the headers, and then calls a
209 function specific to the request type (<command>). Specifically,
210 a request SPAM will be handled by a method do_SPAM(). If no
211 such method exists the server sends an error response to the
212 client. If it exists, it is called with no arguments:
213
214 do_SPAM()
215
216 Note that the request name is case sensitive (i.e. SPAM and spam
217 are different requests).
218
219 The various request details are stored in instance variables:
220
221 - client_address is the client IP address in the form (host,
222 port);
223
224 - command, path and version are the broken-down request line;
225
Barry Warsaw820c1202008-06-12 04:06:45 +0000226 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000227 class) containing the header information;
228
229 - rfile is a file object open for reading positioned at the
230 start of the optional input data part;
231
232 - wfile is a file object open for writing.
233
234 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
235
236 The first thing to be written must be the response line. Then
237 follow 0 or more header lines, then a blank line, and then the
238 actual data (if any). The meaning of the header lines depends on
239 the command executed by the server; in most cases, when data is
240 returned, there should be at least one header line of the form
241
242 Content-type: <type>/<subtype>
243
244 where <type> and <subtype> should be registered MIME types,
245 e.g. "text/html" or "text/plain".
246
247 """
248
249 # The Python system version, truncated to its first component.
250 sys_version = "Python/" + sys.version.split()[0]
251
252 # The server software version. You may want to override this.
253 # The format is multiple whitespace-separated strings,
254 # where each string is of the form name[/version].
255 server_version = "BaseHTTP/" + __version__
256
257 error_message_format = DEFAULT_ERROR_MESSAGE
258 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
259
260 # The default request version. This only affects responses up until
261 # the point where the request line is parsed, so it mainly decides what
262 # the client gets back when sending a malformed request line.
263 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
264 default_request_version = "HTTP/0.9"
265
266 def parse_request(self):
267 """Parse a request (internal).
268
269 The request should be stored in self.raw_requestline; the results
270 are in self.command, self.path, self.request_version and
271 self.headers.
272
273 Return True for success, False for failure; on failure, an
274 error is sent back.
275
276 """
277 self.command = None # set in case of error on the first line
278 self.request_version = version = self.default_request_version
Benjamin Peterson70e28472015-02-17 21:11:10 -0500279 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000280 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800281 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000282 self.requestline = requestline
283 words = requestline.split()
284 if len(words) == 3:
Senthil Kumaran30755492011-12-23 17:03:41 +0800285 command, path, version = words
Georg Brandl24420152008-05-26 16:32:26 +0000286 if version[:5] != 'HTTP/':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200287 self.send_error(
288 HTTPStatus.BAD_REQUEST,
289 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000290 return False
291 try:
292 base_version_number = version.split('/', 1)[1]
293 version_number = base_version_number.split(".")
294 # RFC 2145 section 3.1 says there can be only one "." and
295 # - major and minor numbers MUST be treated as
296 # separate integers;
297 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
298 # turn is lower than HTTP/12.3;
299 # - Leading zeros MUST be ignored by recipients.
300 if len(version_number) != 2:
301 raise ValueError
302 version_number = int(version_number[0]), int(version_number[1])
303 except (ValueError, IndexError):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200304 self.send_error(
305 HTTPStatus.BAD_REQUEST,
306 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000307 return False
308 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
Benjamin Peterson70e28472015-02-17 21:11:10 -0500309 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000310 if version_number >= (2, 0):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200311 self.send_error(
312 HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
313 "Invalid HTTP Version (%s)" % base_version_number)
Georg Brandl24420152008-05-26 16:32:26 +0000314 return False
315 elif len(words) == 2:
Senthil Kumaran30755492011-12-23 17:03:41 +0800316 command, path = words
Benjamin Peterson70e28472015-02-17 21:11:10 -0500317 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000318 if command != 'GET':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200319 self.send_error(
320 HTTPStatus.BAD_REQUEST,
321 "Bad HTTP/0.9 request type (%r)" % command)
Georg Brandl24420152008-05-26 16:32:26 +0000322 return False
323 elif not words:
324 return False
325 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200326 self.send_error(
327 HTTPStatus.BAD_REQUEST,
328 "Bad request syntax (%r)" % requestline)
Georg Brandl24420152008-05-26 16:32:26 +0000329 return False
330 self.command, self.path, self.request_version = command, path, version
331
332 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000333 try:
334 self.headers = http.client.parse_headers(self.rfile,
335 _class=self.MessageClass)
336 except http.client.LineTooLong:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200337 self.send_error(
338 HTTPStatus.BAD_REQUEST,
339 "Line too long")
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000340 return False
Georg Brandl24420152008-05-26 16:32:26 +0000341
342 conntype = self.headers.get('Connection', "")
343 if conntype.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500344 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000345 elif (conntype.lower() == 'keep-alive' and
346 self.protocol_version >= "HTTP/1.1"):
Benjamin Peterson70e28472015-02-17 21:11:10 -0500347 self.close_connection = False
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000348 # Examine the headers and look for an Expect directive
349 expect = self.headers.get('Expect', "")
350 if (expect.lower() == "100-continue" and
351 self.protocol_version >= "HTTP/1.1" and
352 self.request_version >= "HTTP/1.1"):
353 if not self.handle_expect_100():
354 return False
355 return True
356
357 def handle_expect_100(self):
358 """Decide what to do with an "Expect: 100-continue" header.
359
360 If the client is expecting a 100 Continue response, we must
361 respond with either a 100 Continue or a final response before
362 waiting for the request body. The default is to always respond
363 with a 100 Continue. You can behave differently (for example,
364 reject unauthorized requests) by overriding this method.
365
366 This method should either return True (possibly after sending
367 a 100 Continue response) or send an error response and return
368 False.
369
370 """
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200371 self.send_response_only(HTTPStatus.CONTINUE)
Benjamin Peterson04424232014-01-18 21:50:18 -0500372 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000373 return True
374
375 def handle_one_request(self):
376 """Handle a single HTTP request.
377
378 You normally don't need to override this method; see the class
379 __doc__ string for information on how to handle specific HTTP
380 commands such as GET and POST.
381
382 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000383 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000384 self.raw_requestline = self.rfile.readline(65537)
385 if len(self.raw_requestline) > 65536:
386 self.requestline = ''
387 self.request_version = ''
388 self.command = ''
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200389 self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
Antoine Pitrouc4924372010-12-16 16:48:36 +0000390 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000391 if not self.raw_requestline:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500392 self.close_connection = True
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000393 return
394 if not self.parse_request():
395 # An error code has been sent, just exit
396 return
397 mname = 'do_' + self.command
398 if not hasattr(self, mname):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200399 self.send_error(
400 HTTPStatus.NOT_IMPLEMENTED,
401 "Unsupported method (%r)" % self.command)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000402 return
403 method = getattr(self, mname)
404 method()
405 self.wfile.flush() #actually send the response if not already done.
406 except socket.timeout as e:
407 #a read or a write timed out. Discard this connection
408 self.log_error("Request timed out: %r", e)
Benjamin Peterson70e28472015-02-17 21:11:10 -0500409 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000410 return
Georg Brandl24420152008-05-26 16:32:26 +0000411
412 def handle(self):
413 """Handle multiple requests if necessary."""
Benjamin Peterson70e28472015-02-17 21:11:10 -0500414 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000415
416 self.handle_one_request()
417 while not self.close_connection:
418 self.handle_one_request()
419
Senthil Kumaran26886442013-03-15 07:53:21 -0700420 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000421 """Send and log an error reply.
422
Senthil Kumaran26886442013-03-15 07:53:21 -0700423 Arguments are
424 * code: an HTTP error code
425 3 digits
426 * message: a simple optional 1 line reason phrase.
427 *( HTAB / SP / VCHAR / %x80-FF )
428 defaults to short entry matching the response code
429 * explain: a detailed message defaults to the long entry
430 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000431
432 This sends an error response (so it must be called before any
433 output has been generated), logs the error, and finally sends
434 a piece of HTML explaining the error to the user.
435
436 """
437
438 try:
439 shortmsg, longmsg = self.responses[code]
440 except KeyError:
441 shortmsg, longmsg = '???', '???'
442 if message is None:
443 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700444 if explain is None:
445 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000446 self.log_error("code %d, message %s", code, message)
447 # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
448 content = (self.error_message_format %
Senthil Kumaran26886442013-03-15 07:53:21 -0700449 {'code': code, 'message': _quote_html(message), 'explain': _quote_html(explain)})
Senthil Kumaran52d27202012-10-10 23:16:21 -0700450 body = content.encode('UTF-8', 'replace')
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800451 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000452 self.send_header("Content-Type", self.error_content_type)
453 self.send_header('Connection', 'close')
Senthil Kumaran52d27202012-10-10 23:16:21 -0700454 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000455 self.end_headers()
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200456
457 if (self.command != 'HEAD' and
458 code >= 200 and
459 code not in (
460 HTTPStatus.NO_CONTENT, HTTPStatus.NOT_MODIFIED)):
Senthil Kumaran52d27202012-10-10 23:16:21 -0700461 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000462
463 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800464 """Add the response header to the headers buffer and log the
465 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000466
467 Also send two standard headers with the server software
468 version and the current date.
469
470 """
471 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000472 self.send_response_only(code, message)
473 self.send_header('Server', self.version_string())
474 self.send_header('Date', self.date_time_string())
475
476 def send_response_only(self, code, message=None):
477 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000478 if message is None:
479 if code in self.responses:
480 message = self.responses[code][0]
481 else:
482 message = ''
483 if self.request_version != 'HTTP/0.9':
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800484 if not hasattr(self, '_headers_buffer'):
485 self._headers_buffer = []
486 self._headers_buffer.append(("%s %d %s\r\n" %
487 (self.protocol_version, code, message)).encode(
488 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000489
490 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800491 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000492 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000493 if not hasattr(self, '_headers_buffer'):
494 self._headers_buffer = []
495 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000496 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000497
498 if keyword.lower() == 'connection':
499 if value.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500500 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000501 elif value.lower() == 'keep-alive':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500502 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000503
504 def end_headers(self):
505 """Send the blank line ending the MIME headers."""
506 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000507 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800508 self.flush_headers()
509
510 def flush_headers(self):
511 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000512 self.wfile.write(b"".join(self._headers_buffer))
513 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000514
515 def log_request(self, code='-', size='-'):
516 """Log an accepted request.
517
518 This is called by send_response().
519
520 """
Serhiy Storchakac0a23e62015-03-07 11:51:37 +0200521 if isinstance(code, HTTPStatus):
522 code = code.value
Georg Brandl24420152008-05-26 16:32:26 +0000523 self.log_message('"%s" %s %s',
524 self.requestline, str(code), str(size))
525
526 def log_error(self, format, *args):
527 """Log an error.
528
529 This is called when a request cannot be fulfilled. By
530 default it passes the message on to log_message().
531
532 Arguments are the same as for log_message().
533
534 XXX This should go to the separate error log.
535
536 """
537
538 self.log_message(format, *args)
539
540 def log_message(self, format, *args):
541 """Log an arbitrary message.
542
543 This is used by all other logging functions. Override
544 it if you have specific logging wishes.
545
546 The first argument, FORMAT, is a format string for the
547 message to be logged. If the format string contains
548 any % escapes requiring parameters, they should be
549 specified as subsequent arguments (it's just like
550 printf!).
551
Senthil Kumarandb727b42012-04-29 13:41:03 +0800552 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000553 every message.
554
555 """
556
557 sys.stderr.write("%s - - [%s] %s\n" %
558 (self.address_string(),
559 self.log_date_time_string(),
560 format%args))
561
562 def version_string(self):
563 """Return the server software version string."""
564 return self.server_version + ' ' + self.sys_version
565
566 def date_time_string(self, timestamp=None):
567 """Return the current date and time formatted for a message header."""
568 if timestamp is None:
569 timestamp = time.time()
Berker Peksag04bc5b92016-03-14 06:06:03 +0200570 return email.utils.formatdate(timestamp, usegmt=True)
Georg Brandl24420152008-05-26 16:32:26 +0000571
572 def log_date_time_string(self):
573 """Return the current time formatted for logging."""
574 now = time.time()
575 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
576 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
577 day, self.monthname[month], year, hh, mm, ss)
578 return s
579
580 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
581
582 monthname = [None,
583 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
584 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
585
586 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800587 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000588
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800589 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000590
591 # Essentially static class variables
592
593 # The version of the HTTP protocol we support.
594 # Set this to HTTP/1.1 to enable automatic keepalive
595 protocol_version = "HTTP/1.0"
596
Barry Warsaw820c1202008-06-12 04:06:45 +0000597 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000598 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000599
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200600 # hack to maintain backwards compatibility
Georg Brandl24420152008-05-26 16:32:26 +0000601 responses = {
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200602 v: (v.phrase, v.description)
603 for v in HTTPStatus.__members__.values()
604 }
Georg Brandl24420152008-05-26 16:32:26 +0000605
606
607class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
608
609 """Simple HTTP request handler with GET and HEAD commands.
610
611 This serves files from the current directory and any of its
612 subdirectories. The MIME type for files is determined by
613 calling the .guess_type() method.
614
615 The GET and HEAD requests are identical except that the HEAD
616 request omits the actual contents of the file.
617
618 """
619
620 server_version = "SimpleHTTP/" + __version__
621
622 def do_GET(self):
623 """Serve a GET request."""
624 f = self.send_head()
625 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200626 try:
627 self.copyfile(f, self.wfile)
628 finally:
629 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000630
631 def do_HEAD(self):
632 """Serve a HEAD request."""
633 f = self.send_head()
634 if f:
635 f.close()
636
637 def send_head(self):
638 """Common code for GET and HEAD commands.
639
640 This sends the response code and MIME headers.
641
642 Return value is either a file object (which has to be copied
643 to the outputfile by the caller unless the command was HEAD,
644 and must be closed by the caller under all circumstances), or
645 None, in which case the caller has nothing further to do.
646
647 """
648 path = self.translate_path(self.path)
649 f = None
650 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600651 parts = urllib.parse.urlsplit(self.path)
652 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000653 # redirect browser - doing basically what apache does
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200654 self.send_response(HTTPStatus.MOVED_PERMANENTLY)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600655 new_parts = (parts[0], parts[1], parts[2] + '/',
656 parts[3], parts[4])
657 new_url = urllib.parse.urlunsplit(new_parts)
658 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000659 self.end_headers()
660 return None
661 for index in "index.html", "index.htm":
662 index = os.path.join(path, index)
663 if os.path.exists(index):
664 path = index
665 break
666 else:
667 return self.list_directory(path)
668 ctype = self.guess_type(path)
669 try:
670 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200671 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200672 self.send_error(HTTPStatus.NOT_FOUND, "File not found")
Georg Brandl24420152008-05-26 16:32:26 +0000673 return None
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200674 try:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200675 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200676 self.send_header("Content-type", ctype)
677 fs = os.fstat(f.fileno())
678 self.send_header("Content-Length", str(fs[6]))
679 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
680 self.end_headers()
681 return f
682 except:
683 f.close()
684 raise
Georg Brandl24420152008-05-26 16:32:26 +0000685
686 def list_directory(self, path):
687 """Helper to produce a directory listing (absent index.html).
688
689 Return value is either a file object, or None (indicating an
690 error). In either case, the headers are sent, making the
691 interface the same as for send_head().
692
693 """
694 try:
695 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200696 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200697 self.send_error(
698 HTTPStatus.NOT_FOUND,
699 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000700 return None
701 list.sort(key=lambda a: a.lower())
702 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300703 try:
704 displaypath = urllib.parse.unquote(self.path,
705 errors='surrogatepass')
706 except UnicodeDecodeError:
707 displaypath = urllib.parse.unquote(path)
708 displaypath = html.escape(displaypath)
Ezio Melottica897e92011-11-02 19:33:29 +0200709 enc = sys.getfilesystemencoding()
710 title = 'Directory listing for %s' % displaypath
711 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
712 '"http://www.w3.org/TR/html4/strict.dtd">')
713 r.append('<html>\n<head>')
714 r.append('<meta http-equiv="Content-Type" '
715 'content="text/html; charset=%s">' % enc)
716 r.append('<title>%s</title>\n</head>' % title)
717 r.append('<body>\n<h1>%s</h1>' % title)
718 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000719 for name in list:
720 fullname = os.path.join(path, name)
721 displayname = linkname = name
722 # Append / for directories or @ for symbolic links
723 if os.path.isdir(fullname):
724 displayname = name + "/"
725 linkname = name + "/"
726 if os.path.islink(fullname):
727 displayname = name + "@"
728 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200729 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300730 % (urllib.parse.quote(linkname,
731 errors='surrogatepass'),
732 html.escape(displayname)))
Ezio Melottica897e92011-11-02 19:33:29 +0200733 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300734 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000735 f = io.BytesIO()
736 f.write(encoded)
737 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200738 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000739 self.send_header("Content-type", "text/html; charset=%s" % enc)
740 self.send_header("Content-Length", str(len(encoded)))
741 self.end_headers()
742 return f
743
744 def translate_path(self, path):
745 """Translate a /-separated PATH to the local filename syntax.
746
747 Components that mean special things to the local file system
748 (e.g. drive or directory names) are ignored. (XXX They should
749 probably be diagnosed.)
750
751 """
752 # abandon query parameters
753 path = path.split('?',1)[0]
754 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700755 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700756 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300757 try:
758 path = urllib.parse.unquote(path, errors='surrogatepass')
759 except UnicodeDecodeError:
760 path = urllib.parse.unquote(path)
761 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000762 words = path.split('/')
763 words = filter(None, words)
764 path = os.getcwd()
765 for word in words:
766 drive, word = os.path.splitdrive(word)
767 head, word = os.path.split(word)
768 if word in (os.curdir, os.pardir): continue
769 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700770 if trailing_slash:
771 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000772 return path
773
774 def copyfile(self, source, outputfile):
775 """Copy all data between two file objects.
776
777 The SOURCE argument is a file object open for reading
778 (or anything with a read() method) and the DESTINATION
779 argument is a file object open for writing (or
780 anything with a write() method).
781
782 The only reason for overriding this would be to change
783 the block size or perhaps to replace newlines by CRLF
784 -- note however that this the default server uses this
785 to copy binary data as well.
786
787 """
788 shutil.copyfileobj(source, outputfile)
789
790 def guess_type(self, path):
791 """Guess the type of a file.
792
793 Argument is a PATH (a filename).
794
795 Return value is a string of the form type/subtype,
796 usable for a MIME Content-type header.
797
798 The default implementation looks the file's extension
799 up in the table self.extensions_map, using application/octet-stream
800 as a default; however it would be permissible (if
801 slow) to look inside the data to make a better guess.
802
803 """
804
805 base, ext = posixpath.splitext(path)
806 if ext in self.extensions_map:
807 return self.extensions_map[ext]
808 ext = ext.lower()
809 if ext in self.extensions_map:
810 return self.extensions_map[ext]
811 else:
812 return self.extensions_map['']
813
814 if not mimetypes.inited:
815 mimetypes.init() # try to read system mime.types
816 extensions_map = mimetypes.types_map.copy()
817 extensions_map.update({
818 '': 'application/octet-stream', # Default
819 '.py': 'text/plain',
820 '.c': 'text/plain',
821 '.h': 'text/plain',
822 })
823
824
825# Utilities for CGIHTTPRequestHandler
826
Senthil Kumarand70846b2012-04-12 02:34:32 +0800827def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000828 """
829 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000830 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000831
832 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800833 The utility of this function is limited to is_cgi method and helps
834 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000835
Martin Pantercb29e8c2015-10-03 05:55:46 +0000836 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000837
838 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800839
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000840 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000841 # Query component should not be involved.
842 path, _, query = path.partition('?')
843 path = urllib.parse.unquote(path)
844
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000845 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
846 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800847 path_parts = path.split('/')
848 head_parts = []
849 for part in path_parts[:-1]:
850 if part == '..':
851 head_parts.pop() # IndexError if more '..' than prior parts
852 elif part and part != '.':
853 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000854 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800855 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800856 if tail_part:
857 if tail_part == '..':
858 head_parts.pop()
859 tail_part = ''
860 elif tail_part == '.':
861 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000862 else:
863 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800864
Martin Pantercb29e8c2015-10-03 05:55:46 +0000865 if query:
866 tail_part = '?'.join((tail_part, query))
867
Senthil Kumarand70846b2012-04-12 02:34:32 +0800868 splitpath = ('/' + '/'.join(head_parts), tail_part)
869 collapsed_path = "/".join(splitpath)
870
871 return collapsed_path
872
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000873
874
Georg Brandl24420152008-05-26 16:32:26 +0000875nobody = None
876
877def nobody_uid():
878 """Internal routine to get nobody's uid"""
879 global nobody
880 if nobody:
881 return nobody
882 try:
883 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400884 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000885 return -1
886 try:
887 nobody = pwd.getpwnam('nobody')[2]
888 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000889 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000890 return nobody
891
892
893def executable(path):
894 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200895 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000896
897
898class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
899
900 """Complete HTTP server with GET, HEAD and POST commands.
901
902 GET and HEAD also support running CGI scripts.
903
904 The POST command is *only* implemented for CGI scripts.
905
906 """
907
908 # Determine platform specifics
909 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000910
911 # Make rfile unbuffered -- we need to read one line and then pass
912 # the rest to a subprocess, so we can't use buffered input.
913 rbufsize = 0
914
915 def do_POST(self):
916 """Serve a POST request.
917
918 This is only implemented for CGI scripts.
919
920 """
921
922 if self.is_cgi():
923 self.run_cgi()
924 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200925 self.send_error(
926 HTTPStatus.NOT_IMPLEMENTED,
927 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000928
929 def send_head(self):
930 """Version of send_head that support CGI scripts"""
931 if self.is_cgi():
932 return self.run_cgi()
933 else:
934 return SimpleHTTPRequestHandler.send_head(self)
935
936 def is_cgi(self):
937 """Test whether self.path corresponds to a CGI script.
938
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000939 Returns True and updates the cgi_info attribute to the tuple
940 (dir, rest) if self.path requires running a CGI script.
941 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000942
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000943 If any exception is raised, the caller should assume that
944 self.path was rejected as invalid and act accordingly.
945
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000946 The default implementation tests whether the normalized url
947 path begins with one of the strings in self.cgi_directories
948 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000949
950 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000951 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +0800952 dir_sep = collapsed_path.find('/', 1)
953 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800954 if head in self.cgi_directories:
955 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000956 return True
Georg Brandl24420152008-05-26 16:32:26 +0000957 return False
958
Senthil Kumarand70846b2012-04-12 02:34:32 +0800959
Georg Brandl24420152008-05-26 16:32:26 +0000960 cgi_directories = ['/cgi-bin', '/htbin']
961
962 def is_executable(self, path):
963 """Test whether argument path is an executable file."""
964 return executable(path)
965
966 def is_python(self, path):
967 """Test whether argument path is a Python script."""
968 head, tail = os.path.splitext(path)
969 return tail.lower() in (".py", ".pyw")
970
971 def run_cgi(self):
972 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +0000973 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -0700974 path = dir + '/' + rest
975 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000976 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -0700977 nextdir = path[:i]
978 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +0000979
980 scriptdir = self.translate_path(nextdir)
981 if os.path.isdir(scriptdir):
982 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -0700983 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000984 else:
985 break
986
987 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +0000988 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +0000989
990 # dissect the part after the directory name into a script name &
991 # a possible additional path, to be stored in PATH_INFO.
992 i = rest.find('/')
993 if i >= 0:
994 script, rest = rest[:i], rest[i:]
995 else:
996 script, rest = rest, ''
997
998 scriptname = dir + '/' + script
999 scriptfile = self.translate_path(scriptname)
1000 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001001 self.send_error(
1002 HTTPStatus.NOT_FOUND,
1003 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001004 return
1005 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001006 self.send_error(
1007 HTTPStatus.FORBIDDEN,
1008 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001009 return
1010 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001011 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001012 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001013 self.send_error(
1014 HTTPStatus.FORBIDDEN,
1015 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001016 return
1017
1018 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1019 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001020 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001021 env['SERVER_SOFTWARE'] = self.version_string()
1022 env['SERVER_NAME'] = self.server.server_name
1023 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1024 env['SERVER_PROTOCOL'] = self.protocol_version
1025 env['SERVER_PORT'] = str(self.server.server_port)
1026 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001027 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001028 env['PATH_INFO'] = uqrest
1029 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1030 env['SCRIPT_NAME'] = scriptname
1031 if query:
1032 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001033 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001034 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001035 if authorization:
1036 authorization = authorization.split()
1037 if len(authorization) == 2:
1038 import base64, binascii
1039 env['AUTH_TYPE'] = authorization[0]
1040 if authorization[0].lower() == "basic":
1041 try:
1042 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001043 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001044 decode('ascii')
1045 except (binascii.Error, UnicodeError):
1046 pass
1047 else:
1048 authorization = authorization.split(':')
1049 if len(authorization) == 2:
1050 env['REMOTE_USER'] = authorization[0]
1051 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001052 if self.headers.get('content-type') is None:
1053 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001054 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001055 env['CONTENT_TYPE'] = self.headers['content-type']
1056 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001057 if length:
1058 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001059 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001060 if referer:
1061 env['HTTP_REFERER'] = referer
1062 accept = []
1063 for line in self.headers.getallmatchingheaders('accept'):
1064 if line[:1] in "\t\n\r ":
1065 accept.append(line.strip())
1066 else:
1067 accept = accept + line[7:].split(',')
1068 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001069 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001070 if ua:
1071 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001072 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001073 cookie_str = ', '.join(co)
1074 if cookie_str:
1075 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001076 # XXX Other HTTP_* headers
1077 # Since we're setting the env in the parent, provide empty
1078 # values to override previously set values
1079 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1080 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1081 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001082
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001083 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001084 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001085
1086 decoded_query = query.replace('+', ' ')
1087
1088 if self.have_fork:
1089 # Unix -- fork as we should
1090 args = [script]
1091 if '=' not in decoded_query:
1092 args.append(decoded_query)
1093 nobody = nobody_uid()
1094 self.wfile.flush() # Always flush before forking
1095 pid = os.fork()
1096 if pid != 0:
1097 # Parent
1098 pid, sts = os.waitpid(pid, 0)
1099 # throw away additional data [see bug #427345]
1100 while select.select([self.rfile], [], [], 0)[0]:
1101 if not self.rfile.read(1):
1102 break
1103 if sts:
1104 self.log_error("CGI script exit status %#x", sts)
1105 return
1106 # Child
1107 try:
1108 try:
1109 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001110 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001111 pass
1112 os.dup2(self.rfile.fileno(), 0)
1113 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001114 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001115 except:
1116 self.server.handle_error(self.request, self.client_address)
1117 os._exit(127)
1118
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001119 else:
1120 # Non-Unix -- use subprocess
1121 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001122 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001123 if self.is_python(scriptfile):
1124 interp = sys.executable
1125 if interp.lower().endswith("w.exe"):
1126 # On Windows, use python.exe, not pythonw.exe
1127 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001128 cmdline = [interp, '-u'] + cmdline
1129 if '=' not in query:
1130 cmdline.append(query)
1131 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001132 try:
1133 nbytes = int(length)
1134 except (TypeError, ValueError):
1135 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001136 p = subprocess.Popen(cmdline,
1137 stdin=subprocess.PIPE,
1138 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001139 stderr=subprocess.PIPE,
1140 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001141 )
Georg Brandl24420152008-05-26 16:32:26 +00001142 if self.command.lower() == "post" and nbytes > 0:
1143 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001144 else:
1145 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001146 # throw away additional data [see bug #427345]
1147 while select.select([self.rfile._sock], [], [], 0)[0]:
1148 if not self.rfile._sock.recv(1):
1149 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001150 stdout, stderr = p.communicate(data)
1151 self.wfile.write(stdout)
1152 if stderr:
1153 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001154 p.stderr.close()
1155 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001156 status = p.returncode
1157 if status:
1158 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001159 else:
1160 self.log_message("CGI script exited OK")
1161
1162
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001163def test(HandlerClass=BaseHTTPRequestHandler,
1164 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001165 """Test the HTTP request handler class.
1166
Robert Collins9644f242015-08-17 12:18:35 +12001167 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001168
1169 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001170 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001171
1172 HandlerClass.protocol_version = protocol
1173 httpd = ServerClass(server_address, HandlerClass)
1174
1175 sa = httpd.socket.getsockname()
1176 print("Serving HTTP on", sa[0], "port", sa[1], "...")
Alexandre Vassalottib5292a22009-04-03 07:16:55 +00001177 try:
1178 httpd.serve_forever()
1179 except KeyboardInterrupt:
1180 print("\nKeyboard interrupt received, exiting.")
1181 httpd.server_close()
1182 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001183
1184if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001185 parser = argparse.ArgumentParser()
1186 parser.add_argument('--cgi', action='store_true',
1187 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001188 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1189 help='Specify alternate bind address '
1190 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001191 parser.add_argument('port', action='store',
1192 default=8000, type=int,
1193 nargs='?',
1194 help='Specify alternate port [default: 8000]')
1195 args = parser.parse_args()
1196 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001197 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001198 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001199 handler_class = SimpleHTTPRequestHandler
1200 test(HandlerClass=handler_class, port=args.port, bind=args.bind)