blob: 4688096b31c47b47c63a7c48920c4c32f556d23b [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
Georg Brandl1f7fffb2010-10-15 15:57:45 +000090import html
Jeremy Hylton914ab452009-03-27 17:16:06 +000091import http.client
92import io
93import mimetypes
94import os
95import posixpath
96import select
97import shutil
98import socket # For gethostbyaddr()
99import socketserver
100import sys
101import time
102import urllib.parse
Senthil Kumaran42713722010-10-03 17:55:45 +0000103import copy
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800104import argparse
105
Georg Brandl24420152008-05-26 16:32:26 +0000106
107# Default error message template
108DEFAULT_ERROR_MESSAGE = """\
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800109<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
110 "http://www.w3.org/TR/html4/strict.dtd">
Ezio Melottica897e92011-11-02 19:33:29 +0200111<html>
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800112 <head>
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800113 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800114 <title>Error response</title>
115 </head>
116 <body>
117 <h1>Error response</h1>
118 <p>Error code: %(code)d</p>
119 <p>Message: %(message)s.</p>
120 <p>Error code explanation: %(code)s - %(explain)s.</p>
121 </body>
122</html>
Georg Brandl24420152008-05-26 16:32:26 +0000123"""
124
125DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8"
126
127def _quote_html(html):
128 return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
129
130class HTTPServer(socketserver.TCPServer):
131
132 allow_reuse_address = 1 # Seems to make sense in testing environment
133
134 def server_bind(self):
135 """Override server_bind to store the server name."""
136 socketserver.TCPServer.server_bind(self)
137 host, port = self.socket.getsockname()[:2]
138 self.server_name = socket.getfqdn(host)
139 self.server_port = port
140
141
142class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
143
144 """HTTP request handler base class.
145
146 The following explanation of HTTP serves to guide you through the
147 code as well as to expose any misunderstandings I may have about
148 HTTP (so you don't need to read the code to figure out I'm wrong
149 :-).
150
151 HTTP (HyperText Transfer Protocol) is an extensible protocol on
152 top of a reliable stream transport (e.g. TCP/IP). The protocol
153 recognizes three parts to a request:
154
155 1. One line identifying the request type and path
156 2. An optional set of RFC-822-style headers
157 3. An optional data part
158
159 The headers and data are separated by a blank line.
160
161 The first line of the request has the form
162
163 <command> <path> <version>
164
165 where <command> is a (case-sensitive) keyword such as GET or POST,
166 <path> is a string containing path information for the request,
167 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
168 <path> is encoded using the URL encoding scheme (using %xx to signify
169 the ASCII character with hex code xx).
170
171 The specification specifies that lines are separated by CRLF but
172 for compatibility with the widest range of clients recommends
173 servers also handle LF. Similarly, whitespace in the request line
174 is treated sensibly (allowing multiple spaces between components
175 and allowing trailing whitespace).
176
177 Similarly, for output, lines ought to be separated by CRLF pairs
178 but most clients grok LF characters just fine.
179
180 If the first line of the request has the form
181
182 <command> <path>
183
184 (i.e. <version> is left out) then this is assumed to be an HTTP
185 0.9 request; this form has no optional headers and data part and
186 the reply consists of just the data.
187
188 The reply form of the HTTP 1.x protocol again has three parts:
189
190 1. One line giving the response code
191 2. An optional set of RFC-822-style headers
192 3. The data
193
194 Again, the headers and data are separated by a blank line.
195
196 The response code line has the form
197
198 <version> <responsecode> <responsestring>
199
200 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
201 <responsecode> is a 3-digit response code indicating success or
202 failure of the request, and <responsestring> is an optional
203 human-readable string explaining what the response code means.
204
205 This server parses the request and the headers, and then calls a
206 function specific to the request type (<command>). Specifically,
207 a request SPAM will be handled by a method do_SPAM(). If no
208 such method exists the server sends an error response to the
209 client. If it exists, it is called with no arguments:
210
211 do_SPAM()
212
213 Note that the request name is case sensitive (i.e. SPAM and spam
214 are different requests).
215
216 The various request details are stored in instance variables:
217
218 - client_address is the client IP address in the form (host,
219 port);
220
221 - command, path and version are the broken-down request line;
222
Barry Warsaw820c1202008-06-12 04:06:45 +0000223 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000224 class) containing the header information;
225
226 - rfile is a file object open for reading positioned at the
227 start of the optional input data part;
228
229 - wfile is a file object open for writing.
230
231 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
232
233 The first thing to be written must be the response line. Then
234 follow 0 or more header lines, then a blank line, and then the
235 actual data (if any). The meaning of the header lines depends on
236 the command executed by the server; in most cases, when data is
237 returned, there should be at least one header line of the form
238
239 Content-type: <type>/<subtype>
240
241 where <type> and <subtype> should be registered MIME types,
242 e.g. "text/html" or "text/plain".
243
244 """
245
246 # The Python system version, truncated to its first component.
247 sys_version = "Python/" + sys.version.split()[0]
248
249 # The server software version. You may want to override this.
250 # The format is multiple whitespace-separated strings,
251 # where each string is of the form name[/version].
252 server_version = "BaseHTTP/" + __version__
253
254 error_message_format = DEFAULT_ERROR_MESSAGE
255 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
256
257 # The default request version. This only affects responses up until
258 # the point where the request line is parsed, so it mainly decides what
259 # the client gets back when sending a malformed request line.
260 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
261 default_request_version = "HTTP/0.9"
262
263 def parse_request(self):
264 """Parse a request (internal).
265
266 The request should be stored in self.raw_requestline; the results
267 are in self.command, self.path, self.request_version and
268 self.headers.
269
270 Return True for success, False for failure; on failure, an
271 error is sent back.
272
273 """
274 self.command = None # set in case of error on the first line
275 self.request_version = version = self.default_request_version
Benjamin Peterson70e28472015-02-17 21:11:10 -0500276 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000277 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800278 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000279 self.requestline = requestline
280 words = requestline.split()
281 if len(words) == 3:
Senthil Kumaran30755492011-12-23 17:03:41 +0800282 command, path, version = words
Georg Brandl24420152008-05-26 16:32:26 +0000283 if version[:5] != 'HTTP/':
284 self.send_error(400, "Bad request version (%r)" % version)
285 return False
286 try:
287 base_version_number = version.split('/', 1)[1]
288 version_number = base_version_number.split(".")
289 # RFC 2145 section 3.1 says there can be only one "." and
290 # - major and minor numbers MUST be treated as
291 # separate integers;
292 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
293 # turn is lower than HTTP/12.3;
294 # - Leading zeros MUST be ignored by recipients.
295 if len(version_number) != 2:
296 raise ValueError
297 version_number = int(version_number[0]), int(version_number[1])
298 except (ValueError, IndexError):
299 self.send_error(400, "Bad request version (%r)" % version)
300 return False
301 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
Benjamin Peterson70e28472015-02-17 21:11:10 -0500302 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000303 if version_number >= (2, 0):
304 self.send_error(505,
305 "Invalid HTTP Version (%s)" % base_version_number)
306 return False
307 elif len(words) == 2:
Senthil Kumaran30755492011-12-23 17:03:41 +0800308 command, path = words
Benjamin Peterson70e28472015-02-17 21:11:10 -0500309 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000310 if command != 'GET':
311 self.send_error(400,
312 "Bad HTTP/0.9 request type (%r)" % command)
313 return False
314 elif not words:
315 return False
316 else:
317 self.send_error(400, "Bad request syntax (%r)" % requestline)
318 return False
319 self.command, self.path, self.request_version = command, path, version
320
321 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000322 try:
323 self.headers = http.client.parse_headers(self.rfile,
324 _class=self.MessageClass)
325 except http.client.LineTooLong:
326 self.send_error(400, "Line too long")
327 return False
Georg Brandl24420152008-05-26 16:32:26 +0000328
329 conntype = self.headers.get('Connection', "")
330 if conntype.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500331 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000332 elif (conntype.lower() == 'keep-alive' and
333 self.protocol_version >= "HTTP/1.1"):
Benjamin Peterson70e28472015-02-17 21:11:10 -0500334 self.close_connection = False
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000335 # Examine the headers and look for an Expect directive
336 expect = self.headers.get('Expect', "")
337 if (expect.lower() == "100-continue" and
338 self.protocol_version >= "HTTP/1.1" and
339 self.request_version >= "HTTP/1.1"):
340 if not self.handle_expect_100():
341 return False
342 return True
343
344 def handle_expect_100(self):
345 """Decide what to do with an "Expect: 100-continue" header.
346
347 If the client is expecting a 100 Continue response, we must
348 respond with either a 100 Continue or a final response before
349 waiting for the request body. The default is to always respond
350 with a 100 Continue. You can behave differently (for example,
351 reject unauthorized requests) by overriding this method.
352
353 This method should either return True (possibly after sending
354 a 100 Continue response) or send an error response and return
355 False.
356
357 """
358 self.send_response_only(100)
Benjamin Peterson04424232014-01-18 21:50:18 -0500359 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000360 return True
361
362 def handle_one_request(self):
363 """Handle a single HTTP request.
364
365 You normally don't need to override this method; see the class
366 __doc__ string for information on how to handle specific HTTP
367 commands such as GET and POST.
368
369 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000370 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000371 self.raw_requestline = self.rfile.readline(65537)
372 if len(self.raw_requestline) > 65536:
373 self.requestline = ''
374 self.request_version = ''
375 self.command = ''
376 self.send_error(414)
377 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000378 if not self.raw_requestline:
Benjamin Peterson70e28472015-02-17 21:11:10 -0500379 self.close_connection = True
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000380 return
381 if not self.parse_request():
382 # An error code has been sent, just exit
383 return
384 mname = 'do_' + self.command
385 if not hasattr(self, mname):
386 self.send_error(501, "Unsupported method (%r)" % self.command)
387 return
388 method = getattr(self, mname)
389 method()
390 self.wfile.flush() #actually send the response if not already done.
391 except socket.timeout as e:
392 #a read or a write timed out. Discard this connection
393 self.log_error("Request timed out: %r", e)
Benjamin Peterson70e28472015-02-17 21:11:10 -0500394 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000395 return
Georg Brandl24420152008-05-26 16:32:26 +0000396
397 def handle(self):
398 """Handle multiple requests if necessary."""
Benjamin Peterson70e28472015-02-17 21:11:10 -0500399 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000400
401 self.handle_one_request()
402 while not self.close_connection:
403 self.handle_one_request()
404
Senthil Kumaran26886442013-03-15 07:53:21 -0700405 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000406 """Send and log an error reply.
407
Senthil Kumaran26886442013-03-15 07:53:21 -0700408 Arguments are
409 * code: an HTTP error code
410 3 digits
411 * message: a simple optional 1 line reason phrase.
412 *( HTAB / SP / VCHAR / %x80-FF )
413 defaults to short entry matching the response code
414 * explain: a detailed message defaults to the long entry
415 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000416
417 This sends an error response (so it must be called before any
418 output has been generated), logs the error, and finally sends
419 a piece of HTML explaining the error to the user.
420
421 """
422
423 try:
424 shortmsg, longmsg = self.responses[code]
425 except KeyError:
426 shortmsg, longmsg = '???', '???'
427 if message is None:
428 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700429 if explain is None:
430 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000431 self.log_error("code %d, message %s", code, message)
432 # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
433 content = (self.error_message_format %
Senthil Kumaran26886442013-03-15 07:53:21 -0700434 {'code': code, 'message': _quote_html(message), 'explain': _quote_html(explain)})
Senthil Kumaran52d27202012-10-10 23:16:21 -0700435 body = content.encode('UTF-8', 'replace')
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800436 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000437 self.send_header("Content-Type", self.error_content_type)
438 self.send_header('Connection', 'close')
Senthil Kumaran52d27202012-10-10 23:16:21 -0700439 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000440 self.end_headers()
441 if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
Senthil Kumaran52d27202012-10-10 23:16:21 -0700442 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000443
444 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800445 """Add the response header to the headers buffer and log the
446 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000447
448 Also send two standard headers with the server software
449 version and the current date.
450
451 """
452 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000453 self.send_response_only(code, message)
454 self.send_header('Server', self.version_string())
455 self.send_header('Date', self.date_time_string())
456
457 def send_response_only(self, code, message=None):
458 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000459 if message is None:
460 if code in self.responses:
461 message = self.responses[code][0]
462 else:
463 message = ''
464 if self.request_version != 'HTTP/0.9':
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800465 if not hasattr(self, '_headers_buffer'):
466 self._headers_buffer = []
467 self._headers_buffer.append(("%s %d %s\r\n" %
468 (self.protocol_version, code, message)).encode(
469 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000470
471 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800472 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000473 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000474 if not hasattr(self, '_headers_buffer'):
475 self._headers_buffer = []
476 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000477 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000478
479 if keyword.lower() == 'connection':
480 if value.lower() == 'close':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500481 self.close_connection = True
Georg Brandl24420152008-05-26 16:32:26 +0000482 elif value.lower() == 'keep-alive':
Benjamin Peterson70e28472015-02-17 21:11:10 -0500483 self.close_connection = False
Georg Brandl24420152008-05-26 16:32:26 +0000484
485 def end_headers(self):
486 """Send the blank line ending the MIME headers."""
487 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000488 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800489 self.flush_headers()
490
491 def flush_headers(self):
492 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000493 self.wfile.write(b"".join(self._headers_buffer))
494 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000495
496 def log_request(self, code='-', size='-'):
497 """Log an accepted request.
498
499 This is called by send_response().
500
501 """
502
503 self.log_message('"%s" %s %s',
504 self.requestline, str(code), str(size))
505
506 def log_error(self, format, *args):
507 """Log an error.
508
509 This is called when a request cannot be fulfilled. By
510 default it passes the message on to log_message().
511
512 Arguments are the same as for log_message().
513
514 XXX This should go to the separate error log.
515
516 """
517
518 self.log_message(format, *args)
519
520 def log_message(self, format, *args):
521 """Log an arbitrary message.
522
523 This is used by all other logging functions. Override
524 it if you have specific logging wishes.
525
526 The first argument, FORMAT, is a format string for the
527 message to be logged. If the format string contains
528 any % escapes requiring parameters, they should be
529 specified as subsequent arguments (it's just like
530 printf!).
531
Senthil Kumarandb727b42012-04-29 13:41:03 +0800532 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000533 every message.
534
535 """
536
537 sys.stderr.write("%s - - [%s] %s\n" %
538 (self.address_string(),
539 self.log_date_time_string(),
540 format%args))
541
542 def version_string(self):
543 """Return the server software version string."""
544 return self.server_version + ' ' + self.sys_version
545
546 def date_time_string(self, timestamp=None):
547 """Return the current date and time formatted for a message header."""
548 if timestamp is None:
549 timestamp = time.time()
550 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
551 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
552 self.weekdayname[wd],
553 day, self.monthname[month], year,
554 hh, mm, ss)
555 return s
556
557 def log_date_time_string(self):
558 """Return the current time formatted for logging."""
559 now = time.time()
560 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
561 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
562 day, self.monthname[month], year, hh, mm, ss)
563 return s
564
565 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
566
567 monthname = [None,
568 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
569 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
570
571 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800572 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000573
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800574 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000575
576 # Essentially static class variables
577
578 # The version of the HTTP protocol we support.
579 # Set this to HTTP/1.1 to enable automatic keepalive
580 protocol_version = "HTTP/1.0"
581
Barry Warsaw820c1202008-06-12 04:06:45 +0000582 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000583 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000584
585 # Table mapping response codes to messages; entries have the
586 # form {code: (shortmessage, longmessage)}.
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200587 # See RFC 2616 and 6585.
Georg Brandl24420152008-05-26 16:32:26 +0000588 responses = {
589 100: ('Continue', 'Request received, please continue'),
590 101: ('Switching Protocols',
591 'Switching to new protocol; obey Upgrade header'),
592
593 200: ('OK', 'Request fulfilled, document follows'),
594 201: ('Created', 'Document created, URL follows'),
595 202: ('Accepted',
596 'Request accepted, processing continues off-line'),
597 203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
598 204: ('No Content', 'Request fulfilled, nothing follows'),
599 205: ('Reset Content', 'Clear input form for further input.'),
600 206: ('Partial Content', 'Partial content follows.'),
601
602 300: ('Multiple Choices',
603 'Object has several resources -- see URI list'),
604 301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
605 302: ('Found', 'Object moved temporarily -- see URI list'),
606 303: ('See Other', 'Object moved -- see Method and URL list'),
607 304: ('Not Modified',
608 'Document has not changed since given time'),
609 305: ('Use Proxy',
610 'You must use proxy specified in Location to access this '
611 'resource.'),
612 307: ('Temporary Redirect',
613 'Object moved temporarily -- see URI list'),
614
615 400: ('Bad Request',
616 'Bad request syntax or unsupported method'),
617 401: ('Unauthorized',
618 'No permission -- see authorization schemes'),
619 402: ('Payment Required',
620 'No payment -- see charging schemes'),
621 403: ('Forbidden',
622 'Request forbidden -- authorization will not help'),
623 404: ('Not Found', 'Nothing matches the given URI'),
624 405: ('Method Not Allowed',
Senthil Kumaran7aa26212010-02-22 11:00:50 +0000625 'Specified method is invalid for this resource.'),
Georg Brandl24420152008-05-26 16:32:26 +0000626 406: ('Not Acceptable', 'URI not available in preferred format.'),
627 407: ('Proxy Authentication Required', 'You must authenticate with '
628 'this proxy before proceeding.'),
629 408: ('Request Timeout', 'Request timed out; try again later.'),
630 409: ('Conflict', 'Request conflict.'),
631 410: ('Gone',
632 'URI no longer exists and has been permanently removed.'),
633 411: ('Length Required', 'Client must specify Content-Length.'),
634 412: ('Precondition Failed', 'Precondition in headers is false.'),
635 413: ('Request Entity Too Large', 'Entity is too large.'),
636 414: ('Request-URI Too Long', 'URI is too long.'),
637 415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
638 416: ('Requested Range Not Satisfiable',
639 'Cannot satisfy request range.'),
640 417: ('Expectation Failed',
641 'Expect condition could not be satisfied.'),
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200642 428: ('Precondition Required',
643 'The origin server requires the request to be conditional.'),
644 429: ('Too Many Requests', 'The user has sent too many requests '
645 'in a given amount of time ("rate limiting").'),
646 431: ('Request Header Fields Too Large', 'The server is unwilling to '
647 'process the request because its header fields are too large.'),
Georg Brandl24420152008-05-26 16:32:26 +0000648
649 500: ('Internal Server Error', 'Server got itself in trouble'),
650 501: ('Not Implemented',
651 'Server does not support this operation'),
652 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
653 503: ('Service Unavailable',
654 'The server cannot process the request due to a high load'),
655 504: ('Gateway Timeout',
656 'The gateway server did not receive a timely response'),
657 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200658 511: ('Network Authentication Required',
659 'The client needs to authenticate to gain network access.'),
Georg Brandl24420152008-05-26 16:32:26 +0000660 }
661
662
663class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
664
665 """Simple HTTP request handler with GET and HEAD commands.
666
667 This serves files from the current directory and any of its
668 subdirectories. The MIME type for files is determined by
669 calling the .guess_type() method.
670
671 The GET and HEAD requests are identical except that the HEAD
672 request omits the actual contents of the file.
673
674 """
675
676 server_version = "SimpleHTTP/" + __version__
677
678 def do_GET(self):
679 """Serve a GET request."""
680 f = self.send_head()
681 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200682 try:
683 self.copyfile(f, self.wfile)
684 finally:
685 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000686
687 def do_HEAD(self):
688 """Serve a HEAD request."""
689 f = self.send_head()
690 if f:
691 f.close()
692
693 def send_head(self):
694 """Common code for GET and HEAD commands.
695
696 This sends the response code and MIME headers.
697
698 Return value is either a file object (which has to be copied
699 to the outputfile by the caller unless the command was HEAD,
700 and must be closed by the caller under all circumstances), or
701 None, in which case the caller has nothing further to do.
702
703 """
704 path = self.translate_path(self.path)
705 f = None
706 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600707 parts = urllib.parse.urlsplit(self.path)
708 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000709 # redirect browser - doing basically what apache does
710 self.send_response(301)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600711 new_parts = (parts[0], parts[1], parts[2] + '/',
712 parts[3], parts[4])
713 new_url = urllib.parse.urlunsplit(new_parts)
714 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000715 self.end_headers()
716 return None
717 for index in "index.html", "index.htm":
718 index = os.path.join(path, index)
719 if os.path.exists(index):
720 path = index
721 break
722 else:
723 return self.list_directory(path)
724 ctype = self.guess_type(path)
725 try:
726 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200727 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +0000728 self.send_error(404, "File not found")
729 return None
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200730 try:
731 self.send_response(200)
732 self.send_header("Content-type", ctype)
733 fs = os.fstat(f.fileno())
734 self.send_header("Content-Length", str(fs[6]))
735 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
736 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:
Georg Brandl24420152008-05-26 16:32:26 +0000753 self.send_error(404, "No permission to list directory")
754 return None
755 list.sort(key=lambda a: a.lower())
756 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300757 try:
758 displaypath = urllib.parse.unquote(self.path,
759 errors='surrogatepass')
760 except UnicodeDecodeError:
761 displaypath = urllib.parse.unquote(path)
762 displaypath = html.escape(displaypath)
Ezio Melottica897e92011-11-02 19:33:29 +0200763 enc = sys.getfilesystemencoding()
764 title = 'Directory listing for %s' % displaypath
765 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
766 '"http://www.w3.org/TR/html4/strict.dtd">')
767 r.append('<html>\n<head>')
768 r.append('<meta http-equiv="Content-Type" '
769 'content="text/html; charset=%s">' % enc)
770 r.append('<title>%s</title>\n</head>' % title)
771 r.append('<body>\n<h1>%s</h1>' % title)
772 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000773 for name in list:
774 fullname = os.path.join(path, name)
775 displayname = linkname = name
776 # Append / for directories or @ for symbolic links
777 if os.path.isdir(fullname):
778 displayname = name + "/"
779 linkname = name + "/"
780 if os.path.islink(fullname):
781 displayname = name + "@"
782 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200783 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300784 % (urllib.parse.quote(linkname,
785 errors='surrogatepass'),
786 html.escape(displayname)))
Ezio Melottica897e92011-11-02 19:33:29 +0200787 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300788 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000789 f = io.BytesIO()
790 f.write(encoded)
791 f.seek(0)
792 self.send_response(200)
793 self.send_header("Content-type", "text/html; charset=%s" % enc)
794 self.send_header("Content-Length", str(len(encoded)))
795 self.end_headers()
796 return f
797
798 def translate_path(self, path):
799 """Translate a /-separated PATH to the local filename syntax.
800
801 Components that mean special things to the local file system
802 (e.g. drive or directory names) are ignored. (XXX They should
803 probably be diagnosed.)
804
805 """
806 # abandon query parameters
807 path = path.split('?',1)[0]
808 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700809 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700810 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300811 try:
812 path = urllib.parse.unquote(path, errors='surrogatepass')
813 except UnicodeDecodeError:
814 path = urllib.parse.unquote(path)
815 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000816 words = path.split('/')
817 words = filter(None, words)
818 path = os.getcwd()
819 for word in words:
820 drive, word = os.path.splitdrive(word)
821 head, word = os.path.split(word)
822 if word in (os.curdir, os.pardir): continue
823 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700824 if trailing_slash:
825 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000826 return path
827
828 def copyfile(self, source, outputfile):
829 """Copy all data between two file objects.
830
831 The SOURCE argument is a file object open for reading
832 (or anything with a read() method) and the DESTINATION
833 argument is a file object open for writing (or
834 anything with a write() method).
835
836 The only reason for overriding this would be to change
837 the block size or perhaps to replace newlines by CRLF
838 -- note however that this the default server uses this
839 to copy binary data as well.
840
841 """
842 shutil.copyfileobj(source, outputfile)
843
844 def guess_type(self, path):
845 """Guess the type of a file.
846
847 Argument is a PATH (a filename).
848
849 Return value is a string of the form type/subtype,
850 usable for a MIME Content-type header.
851
852 The default implementation looks the file's extension
853 up in the table self.extensions_map, using application/octet-stream
854 as a default; however it would be permissible (if
855 slow) to look inside the data to make a better guess.
856
857 """
858
859 base, ext = posixpath.splitext(path)
860 if ext in self.extensions_map:
861 return self.extensions_map[ext]
862 ext = ext.lower()
863 if ext in self.extensions_map:
864 return self.extensions_map[ext]
865 else:
866 return self.extensions_map['']
867
868 if not mimetypes.inited:
869 mimetypes.init() # try to read system mime.types
870 extensions_map = mimetypes.types_map.copy()
871 extensions_map.update({
872 '': 'application/octet-stream', # Default
873 '.py': 'text/plain',
874 '.c': 'text/plain',
875 '.h': 'text/plain',
876 })
877
878
879# Utilities for CGIHTTPRequestHandler
880
Senthil Kumarand70846b2012-04-12 02:34:32 +0800881def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000882 """
883 Given a URL path, remove extra '/'s and '.' path elements and collapse
Martin Panter9955a372015-10-07 10:26:23 +0000884 any '..' references and returns a collapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000885
886 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800887 The utility of this function is limited to is_cgi method and helps
888 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000889
Martin Pantercb29e8c2015-10-03 05:55:46 +0000890 Returns: The reconstituted URL, which will always start with a '/'.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000891
892 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800893
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000894 """
Martin Pantercb29e8c2015-10-03 05:55:46 +0000895 # Query component should not be involved.
896 path, _, query = path.partition('?')
897 path = urllib.parse.unquote(path)
898
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000899 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
900 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800901 path_parts = path.split('/')
902 head_parts = []
903 for part in path_parts[:-1]:
904 if part == '..':
905 head_parts.pop() # IndexError if more '..' than prior parts
906 elif part and part != '.':
907 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000908 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800909 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800910 if tail_part:
911 if tail_part == '..':
912 head_parts.pop()
913 tail_part = ''
914 elif tail_part == '.':
915 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000916 else:
917 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800918
Martin Pantercb29e8c2015-10-03 05:55:46 +0000919 if query:
920 tail_part = '?'.join((tail_part, query))
921
Senthil Kumarand70846b2012-04-12 02:34:32 +0800922 splitpath = ('/' + '/'.join(head_parts), tail_part)
923 collapsed_path = "/".join(splitpath)
924
925 return collapsed_path
926
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000927
928
Georg Brandl24420152008-05-26 16:32:26 +0000929nobody = None
930
931def nobody_uid():
932 """Internal routine to get nobody's uid"""
933 global nobody
934 if nobody:
935 return nobody
936 try:
937 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400938 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000939 return -1
940 try:
941 nobody = pwd.getpwnam('nobody')[2]
942 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000943 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000944 return nobody
945
946
947def executable(path):
948 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200949 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000950
951
952class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
953
954 """Complete HTTP server with GET, HEAD and POST commands.
955
956 GET and HEAD also support running CGI scripts.
957
958 The POST command is *only* implemented for CGI scripts.
959
960 """
961
962 # Determine platform specifics
963 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000964
965 # Make rfile unbuffered -- we need to read one line and then pass
966 # the rest to a subprocess, so we can't use buffered input.
967 rbufsize = 0
968
969 def do_POST(self):
970 """Serve a POST request.
971
972 This is only implemented for CGI scripts.
973
974 """
975
976 if self.is_cgi():
977 self.run_cgi()
978 else:
979 self.send_error(501, "Can only POST to CGI scripts")
980
981 def send_head(self):
982 """Version of send_head that support CGI scripts"""
983 if self.is_cgi():
984 return self.run_cgi()
985 else:
986 return SimpleHTTPRequestHandler.send_head(self)
987
988 def is_cgi(self):
989 """Test whether self.path corresponds to a CGI script.
990
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000991 Returns True and updates the cgi_info attribute to the tuple
992 (dir, rest) if self.path requires running a CGI script.
993 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000994
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000995 If any exception is raised, the caller should assume that
996 self.path was rejected as invalid and act accordingly.
997
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000998 The default implementation tests whether the normalized url
999 path begins with one of the strings in self.cgi_directories
1000 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +00001001
1002 """
Martin Pantercb29e8c2015-10-03 05:55:46 +00001003 collapsed_path = _url_collapse_path(self.path)
Senthil Kumarand70846b2012-04-12 02:34:32 +08001004 dir_sep = collapsed_path.find('/', 1)
1005 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +08001006 if head in self.cgi_directories:
1007 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +00001008 return True
Georg Brandl24420152008-05-26 16:32:26 +00001009 return False
1010
Senthil Kumarand70846b2012-04-12 02:34:32 +08001011
Georg Brandl24420152008-05-26 16:32:26 +00001012 cgi_directories = ['/cgi-bin', '/htbin']
1013
1014 def is_executable(self, path):
1015 """Test whether argument path is an executable file."""
1016 return executable(path)
1017
1018 def is_python(self, path):
1019 """Test whether argument path is a Python script."""
1020 head, tail = os.path.splitext(path)
1021 return tail.lower() in (".py", ".pyw")
1022
1023 def run_cgi(self):
1024 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +00001025 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -07001026 path = dir + '/' + rest
1027 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001028 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -07001029 nextdir = path[:i]
1030 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +00001031
1032 scriptdir = self.translate_path(nextdir)
1033 if os.path.isdir(scriptdir):
1034 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -07001035 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001036 else:
1037 break
1038
1039 # find an explicit query string, if present.
Martin Pantera02e18a2015-10-03 05:38:07 +00001040 rest, _, query = rest.partition('?')
Georg Brandl24420152008-05-26 16:32:26 +00001041
1042 # dissect the part after the directory name into a script name &
1043 # a possible additional path, to be stored in PATH_INFO.
1044 i = rest.find('/')
1045 if i >= 0:
1046 script, rest = rest[:i], rest[i:]
1047 else:
1048 script, rest = rest, ''
1049
1050 scriptname = dir + '/' + script
1051 scriptfile = self.translate_path(scriptname)
1052 if not os.path.exists(scriptfile):
1053 self.send_error(404, "No such CGI script (%r)" % scriptname)
1054 return
1055 if not os.path.isfile(scriptfile):
1056 self.send_error(403, "CGI script is not a plain file (%r)" %
1057 scriptname)
1058 return
1059 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001060 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001061 if not self.is_executable(scriptfile):
1062 self.send_error(403, "CGI script is not executable (%r)" %
1063 scriptname)
1064 return
1065
1066 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1067 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001068 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001069 env['SERVER_SOFTWARE'] = self.version_string()
1070 env['SERVER_NAME'] = self.server.server_name
1071 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1072 env['SERVER_PROTOCOL'] = self.protocol_version
1073 env['SERVER_PORT'] = str(self.server.server_port)
1074 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001075 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001076 env['PATH_INFO'] = uqrest
1077 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1078 env['SCRIPT_NAME'] = scriptname
1079 if query:
1080 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001081 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001082 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001083 if authorization:
1084 authorization = authorization.split()
1085 if len(authorization) == 2:
1086 import base64, binascii
1087 env['AUTH_TYPE'] = authorization[0]
1088 if authorization[0].lower() == "basic":
1089 try:
1090 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001091 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001092 decode('ascii')
1093 except (binascii.Error, UnicodeError):
1094 pass
1095 else:
1096 authorization = authorization.split(':')
1097 if len(authorization) == 2:
1098 env['REMOTE_USER'] = authorization[0]
1099 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001100 if self.headers.get('content-type') is None:
1101 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001102 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001103 env['CONTENT_TYPE'] = self.headers['content-type']
1104 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001105 if length:
1106 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001107 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001108 if referer:
1109 env['HTTP_REFERER'] = referer
1110 accept = []
1111 for line in self.headers.getallmatchingheaders('accept'):
1112 if line[:1] in "\t\n\r ":
1113 accept.append(line.strip())
1114 else:
1115 accept = accept + line[7:].split(',')
1116 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001117 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001118 if ua:
1119 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001120 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001121 cookie_str = ', '.join(co)
1122 if cookie_str:
1123 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001124 # XXX Other HTTP_* headers
1125 # Since we're setting the env in the parent, provide empty
1126 # values to override previously set values
1127 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1128 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1129 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001130
1131 self.send_response(200, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001132 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001133
1134 decoded_query = query.replace('+', ' ')
1135
1136 if self.have_fork:
1137 # Unix -- fork as we should
1138 args = [script]
1139 if '=' not in decoded_query:
1140 args.append(decoded_query)
1141 nobody = nobody_uid()
1142 self.wfile.flush() # Always flush before forking
1143 pid = os.fork()
1144 if pid != 0:
1145 # Parent
1146 pid, sts = os.waitpid(pid, 0)
1147 # throw away additional data [see bug #427345]
1148 while select.select([self.rfile], [], [], 0)[0]:
1149 if not self.rfile.read(1):
1150 break
1151 if sts:
1152 self.log_error("CGI script exit status %#x", sts)
1153 return
1154 # Child
1155 try:
1156 try:
1157 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001158 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001159 pass
1160 os.dup2(self.rfile.fileno(), 0)
1161 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001162 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001163 except:
1164 self.server.handle_error(self.request, self.client_address)
1165 os._exit(127)
1166
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001167 else:
1168 # Non-Unix -- use subprocess
1169 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001170 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001171 if self.is_python(scriptfile):
1172 interp = sys.executable
1173 if interp.lower().endswith("w.exe"):
1174 # On Windows, use python.exe, not pythonw.exe
1175 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001176 cmdline = [interp, '-u'] + cmdline
1177 if '=' not in query:
1178 cmdline.append(query)
1179 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001180 try:
1181 nbytes = int(length)
1182 except (TypeError, ValueError):
1183 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001184 p = subprocess.Popen(cmdline,
1185 stdin=subprocess.PIPE,
1186 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001187 stderr=subprocess.PIPE,
1188 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001189 )
Georg Brandl24420152008-05-26 16:32:26 +00001190 if self.command.lower() == "post" and nbytes > 0:
1191 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001192 else:
1193 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001194 # throw away additional data [see bug #427345]
1195 while select.select([self.rfile._sock], [], [], 0)[0]:
1196 if not self.rfile._sock.recv(1):
1197 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001198 stdout, stderr = p.communicate(data)
1199 self.wfile.write(stdout)
1200 if stderr:
1201 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001202 p.stderr.close()
1203 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001204 status = p.returncode
1205 if status:
1206 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001207 else:
1208 self.log_message("CGI script exited OK")
1209
1210
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001211def test(HandlerClass=BaseHTTPRequestHandler,
1212 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001213 """Test the HTTP request handler class.
1214
Robert Collins9644f242015-08-17 12:18:35 +12001215 This runs an HTTP server on port 8000 (or the port argument).
Georg Brandl24420152008-05-26 16:32:26 +00001216
1217 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001218 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001219
1220 HandlerClass.protocol_version = protocol
1221 httpd = ServerClass(server_address, HandlerClass)
1222
1223 sa = httpd.socket.getsockname()
1224 print("Serving HTTP on", sa[0], "port", sa[1], "...")
Alexandre Vassalottib5292a22009-04-03 07:16:55 +00001225 try:
1226 httpd.serve_forever()
1227 except KeyboardInterrupt:
1228 print("\nKeyboard interrupt received, exiting.")
1229 httpd.server_close()
1230 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001231
1232if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001233 parser = argparse.ArgumentParser()
1234 parser.add_argument('--cgi', action='store_true',
1235 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001236 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1237 help='Specify alternate bind address '
1238 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001239 parser.add_argument('port', action='store',
1240 default=8000, type=int,
1241 nargs='?',
1242 help='Specify alternate port [default: 8000]')
1243 args = parser.parse_args()
1244 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001245 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001246 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001247 handler_class = SimpleHTTPRequestHandler
1248 test(HandlerClass=handler_class, port=args.port, bind=args.bind)