blob: dab1eb65c4bc6327f4b1d7f2578612c0b5cec92e [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
85__all__ = ["HTTPServer", "BaseHTTPRequestHandler"]
86
Georg Brandl1f7fffb2010-10-15 15:57:45 +000087import html
Barry Warsaw820c1202008-06-12 04:06:45 +000088import email.message
89import email.parser
Jeremy Hylton914ab452009-03-27 17:16:06 +000090import http.client
91import io
92import mimetypes
93import os
94import posixpath
95import select
96import shutil
97import socket # For gethostbyaddr()
98import socketserver
99import sys
100import time
101import urllib.parse
Senthil Kumaran42713722010-10-03 17:55:45 +0000102import copy
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800103import argparse
104
Georg Brandl24420152008-05-26 16:32:26 +0000105
106# Default error message template
107DEFAULT_ERROR_MESSAGE = """\
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800108<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
109 "http://www.w3.org/TR/html4/strict.dtd">
Ezio Melottica897e92011-11-02 19:33:29 +0200110<html>
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800111 <head>
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800112 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800113 <title>Error response</title>
114 </head>
115 <body>
116 <h1>Error response</h1>
117 <p>Error code: %(code)d</p>
118 <p>Message: %(message)s.</p>
119 <p>Error code explanation: %(code)s - %(explain)s.</p>
120 </body>
121</html>
Georg Brandl24420152008-05-26 16:32:26 +0000122"""
123
124DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8"
125
126def _quote_html(html):
127 return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
128
129class HTTPServer(socketserver.TCPServer):
130
131 allow_reuse_address = 1 # Seems to make sense in testing environment
132
133 def server_bind(self):
134 """Override server_bind to store the server name."""
135 socketserver.TCPServer.server_bind(self)
136 host, port = self.socket.getsockname()[:2]
137 self.server_name = socket.getfqdn(host)
138 self.server_port = port
139
140
141class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
142
143 """HTTP request handler base class.
144
145 The following explanation of HTTP serves to guide you through the
146 code as well as to expose any misunderstandings I may have about
147 HTTP (so you don't need to read the code to figure out I'm wrong
148 :-).
149
150 HTTP (HyperText Transfer Protocol) is an extensible protocol on
151 top of a reliable stream transport (e.g. TCP/IP). The protocol
152 recognizes three parts to a request:
153
154 1. One line identifying the request type and path
155 2. An optional set of RFC-822-style headers
156 3. An optional data part
157
158 The headers and data are separated by a blank line.
159
160 The first line of the request has the form
161
162 <command> <path> <version>
163
164 where <command> is a (case-sensitive) keyword such as GET or POST,
165 <path> is a string containing path information for the request,
166 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
167 <path> is encoded using the URL encoding scheme (using %xx to signify
168 the ASCII character with hex code xx).
169
170 The specification specifies that lines are separated by CRLF but
171 for compatibility with the widest range of clients recommends
172 servers also handle LF. Similarly, whitespace in the request line
173 is treated sensibly (allowing multiple spaces between components
174 and allowing trailing whitespace).
175
176 Similarly, for output, lines ought to be separated by CRLF pairs
177 but most clients grok LF characters just fine.
178
179 If the first line of the request has the form
180
181 <command> <path>
182
183 (i.e. <version> is left out) then this is assumed to be an HTTP
184 0.9 request; this form has no optional headers and data part and
185 the reply consists of just the data.
186
187 The reply form of the HTTP 1.x protocol again has three parts:
188
189 1. One line giving the response code
190 2. An optional set of RFC-822-style headers
191 3. The data
192
193 Again, the headers and data are separated by a blank line.
194
195 The response code line has the form
196
197 <version> <responsecode> <responsestring>
198
199 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
200 <responsecode> is a 3-digit response code indicating success or
201 failure of the request, and <responsestring> is an optional
202 human-readable string explaining what the response code means.
203
204 This server parses the request and the headers, and then calls a
205 function specific to the request type (<command>). Specifically,
206 a request SPAM will be handled by a method do_SPAM(). If no
207 such method exists the server sends an error response to the
208 client. If it exists, it is called with no arguments:
209
210 do_SPAM()
211
212 Note that the request name is case sensitive (i.e. SPAM and spam
213 are different requests).
214
215 The various request details are stored in instance variables:
216
217 - client_address is the client IP address in the form (host,
218 port);
219
220 - command, path and version are the broken-down request line;
221
Barry Warsaw820c1202008-06-12 04:06:45 +0000222 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000223 class) containing the header information;
224
225 - rfile is a file object open for reading positioned at the
226 start of the optional input data part;
227
228 - wfile is a file object open for writing.
229
230 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
231
232 The first thing to be written must be the response line. Then
233 follow 0 or more header lines, then a blank line, and then the
234 actual data (if any). The meaning of the header lines depends on
235 the command executed by the server; in most cases, when data is
236 returned, there should be at least one header line of the form
237
238 Content-type: <type>/<subtype>
239
240 where <type> and <subtype> should be registered MIME types,
241 e.g. "text/html" or "text/plain".
242
243 """
244
245 # The Python system version, truncated to its first component.
246 sys_version = "Python/" + sys.version.split()[0]
247
248 # The server software version. You may want to override this.
249 # The format is multiple whitespace-separated strings,
250 # where each string is of the form name[/version].
251 server_version = "BaseHTTP/" + __version__
252
253 error_message_format = DEFAULT_ERROR_MESSAGE
254 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
255
256 # The default request version. This only affects responses up until
257 # the point where the request line is parsed, so it mainly decides what
258 # the client gets back when sending a malformed request line.
259 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
260 default_request_version = "HTTP/0.9"
261
262 def parse_request(self):
263 """Parse a request (internal).
264
265 The request should be stored in self.raw_requestline; the results
266 are in self.command, self.path, self.request_version and
267 self.headers.
268
269 Return True for success, False for failure; on failure, an
270 error is sent back.
271
272 """
273 self.command = None # set in case of error on the first line
274 self.request_version = version = self.default_request_version
275 self.close_connection = 1
276 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800277 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000278 self.requestline = requestline
279 words = requestline.split()
280 if len(words) == 3:
Senthil Kumaran30755492011-12-23 17:03:41 +0800281 command, path, version = words
Georg Brandl24420152008-05-26 16:32:26 +0000282 if version[:5] != 'HTTP/':
283 self.send_error(400, "Bad request version (%r)" % version)
284 return False
285 try:
286 base_version_number = version.split('/', 1)[1]
287 version_number = base_version_number.split(".")
288 # RFC 2145 section 3.1 says there can be only one "." and
289 # - major and minor numbers MUST be treated as
290 # separate integers;
291 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
292 # turn is lower than HTTP/12.3;
293 # - Leading zeros MUST be ignored by recipients.
294 if len(version_number) != 2:
295 raise ValueError
296 version_number = int(version_number[0]), int(version_number[1])
297 except (ValueError, IndexError):
298 self.send_error(400, "Bad request version (%r)" % version)
299 return False
300 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
301 self.close_connection = 0
302 if version_number >= (2, 0):
303 self.send_error(505,
304 "Invalid HTTP Version (%s)" % base_version_number)
305 return False
306 elif len(words) == 2:
Senthil Kumaran30755492011-12-23 17:03:41 +0800307 command, path = words
Georg Brandl24420152008-05-26 16:32:26 +0000308 self.close_connection = 1
309 if command != 'GET':
310 self.send_error(400,
311 "Bad HTTP/0.9 request type (%r)" % command)
312 return False
313 elif not words:
314 return False
315 else:
316 self.send_error(400, "Bad request syntax (%r)" % requestline)
317 return False
318 self.command, self.path, self.request_version = command, path, version
319
320 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000321 try:
322 self.headers = http.client.parse_headers(self.rfile,
323 _class=self.MessageClass)
324 except http.client.LineTooLong:
325 self.send_error(400, "Line too long")
326 return False
Georg Brandl24420152008-05-26 16:32:26 +0000327
328 conntype = self.headers.get('Connection', "")
329 if conntype.lower() == 'close':
330 self.close_connection = 1
331 elif (conntype.lower() == 'keep-alive' and
332 self.protocol_version >= "HTTP/1.1"):
333 self.close_connection = 0
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000334 # Examine the headers and look for an Expect directive
335 expect = self.headers.get('Expect', "")
336 if (expect.lower() == "100-continue" and
337 self.protocol_version >= "HTTP/1.1" and
338 self.request_version >= "HTTP/1.1"):
339 if not self.handle_expect_100():
340 return False
341 return True
342
343 def handle_expect_100(self):
344 """Decide what to do with an "Expect: 100-continue" header.
345
346 If the client is expecting a 100 Continue response, we must
347 respond with either a 100 Continue or a final response before
348 waiting for the request body. The default is to always respond
349 with a 100 Continue. You can behave differently (for example,
350 reject unauthorized requests) by overriding this method.
351
352 This method should either return True (possibly after sending
353 a 100 Continue response) or send an error response and return
354 False.
355
356 """
357 self.send_response_only(100)
Benjamin Peterson04424232014-01-18 21:50:18 -0500358 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000359 return True
360
361 def handle_one_request(self):
362 """Handle a single HTTP request.
363
364 You normally don't need to override this method; see the class
365 __doc__ string for information on how to handle specific HTTP
366 commands such as GET and POST.
367
368 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000369 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000370 self.raw_requestline = self.rfile.readline(65537)
371 if len(self.raw_requestline) > 65536:
372 self.requestline = ''
373 self.request_version = ''
374 self.command = ''
375 self.send_error(414)
376 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000377 if not self.raw_requestline:
378 self.close_connection = 1
379 return
380 if not self.parse_request():
381 # An error code has been sent, just exit
382 return
383 mname = 'do_' + self.command
384 if not hasattr(self, mname):
385 self.send_error(501, "Unsupported method (%r)" % self.command)
386 return
387 method = getattr(self, mname)
388 method()
389 self.wfile.flush() #actually send the response if not already done.
390 except socket.timeout as e:
391 #a read or a write timed out. Discard this connection
392 self.log_error("Request timed out: %r", e)
Georg Brandl24420152008-05-26 16:32:26 +0000393 self.close_connection = 1
394 return
Georg Brandl24420152008-05-26 16:32:26 +0000395
396 def handle(self):
397 """Handle multiple requests if necessary."""
398 self.close_connection = 1
399
400 self.handle_one_request()
401 while not self.close_connection:
402 self.handle_one_request()
403
404 def send_error(self, code, message=None):
405 """Send and log an error reply.
406
407 Arguments are the error code, and a detailed message.
408 The detailed message defaults to the short entry matching the
409 response code.
410
411 This sends an error response (so it must be called before any
412 output has been generated), logs the error, and finally sends
413 a piece of HTML explaining the error to the user.
414
415 """
416
417 try:
418 shortmsg, longmsg = self.responses[code]
419 except KeyError:
420 shortmsg, longmsg = '???', '???'
421 if message is None:
422 message = shortmsg
423 explain = longmsg
424 self.log_error("code %d, message %s", code, message)
425 # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
426 content = (self.error_message_format %
427 {'code': code, 'message': _quote_html(message), 'explain': explain})
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800428 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000429 self.send_header("Content-Type", self.error_content_type)
430 self.send_header('Connection', 'close')
431 self.end_headers()
432 if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
433 self.wfile.write(content.encode('UTF-8', 'replace'))
434
435 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800436 """Add the response header to the headers buffer and log the
437 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000438
439 Also send two standard headers with the server software
440 version and the current date.
441
442 """
443 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000444 self.send_response_only(code, message)
445 self.send_header('Server', self.version_string())
446 self.send_header('Date', self.date_time_string())
447
448 def send_response_only(self, code, message=None):
449 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000450 if message is None:
451 if code in self.responses:
452 message = self.responses[code][0]
453 else:
454 message = ''
455 if self.request_version != 'HTTP/0.9':
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800456 if not hasattr(self, '_headers_buffer'):
457 self._headers_buffer = []
458 self._headers_buffer.append(("%s %d %s\r\n" %
459 (self.protocol_version, code, message)).encode(
460 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000461
462 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800463 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000464 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000465 if not hasattr(self, '_headers_buffer'):
466 self._headers_buffer = []
467 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000468 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000469
470 if keyword.lower() == 'connection':
471 if value.lower() == 'close':
472 self.close_connection = 1
473 elif value.lower() == 'keep-alive':
474 self.close_connection = 0
475
476 def end_headers(self):
477 """Send the blank line ending the MIME headers."""
478 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000479 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800480 self.flush_headers()
481
482 def flush_headers(self):
483 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000484 self.wfile.write(b"".join(self._headers_buffer))
485 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000486
487 def log_request(self, code='-', size='-'):
488 """Log an accepted request.
489
490 This is called by send_response().
491
492 """
493
494 self.log_message('"%s" %s %s',
495 self.requestline, str(code), str(size))
496
497 def log_error(self, format, *args):
498 """Log an error.
499
500 This is called when a request cannot be fulfilled. By
501 default it passes the message on to log_message().
502
503 Arguments are the same as for log_message().
504
505 XXX This should go to the separate error log.
506
507 """
508
509 self.log_message(format, *args)
510
511 def log_message(self, format, *args):
512 """Log an arbitrary message.
513
514 This is used by all other logging functions. Override
515 it if you have specific logging wishes.
516
517 The first argument, FORMAT, is a format string for the
518 message to be logged. If the format string contains
519 any % escapes requiring parameters, they should be
520 specified as subsequent arguments (it's just like
521 printf!).
522
Senthil Kumarandb727b42012-04-29 13:41:03 +0800523 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000524 every message.
525
526 """
527
528 sys.stderr.write("%s - - [%s] %s\n" %
529 (self.address_string(),
530 self.log_date_time_string(),
531 format%args))
532
533 def version_string(self):
534 """Return the server software version string."""
535 return self.server_version + ' ' + self.sys_version
536
537 def date_time_string(self, timestamp=None):
538 """Return the current date and time formatted for a message header."""
539 if timestamp is None:
540 timestamp = time.time()
541 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
542 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
543 self.weekdayname[wd],
544 day, self.monthname[month], year,
545 hh, mm, ss)
546 return s
547
548 def log_date_time_string(self):
549 """Return the current time formatted for logging."""
550 now = time.time()
551 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
552 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
553 day, self.monthname[month], year, hh, mm, ss)
554 return s
555
556 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
557
558 monthname = [None,
559 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
560 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
561
562 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800563 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000564
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800565 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000566
567 # Essentially static class variables
568
569 # The version of the HTTP protocol we support.
570 # Set this to HTTP/1.1 to enable automatic keepalive
571 protocol_version = "HTTP/1.0"
572
Barry Warsaw820c1202008-06-12 04:06:45 +0000573 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000574 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000575
576 # Table mapping response codes to messages; entries have the
577 # form {code: (shortmessage, longmessage)}.
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200578 # See RFC 2616 and 6585.
Georg Brandl24420152008-05-26 16:32:26 +0000579 responses = {
580 100: ('Continue', 'Request received, please continue'),
581 101: ('Switching Protocols',
582 'Switching to new protocol; obey Upgrade header'),
583
584 200: ('OK', 'Request fulfilled, document follows'),
585 201: ('Created', 'Document created, URL follows'),
586 202: ('Accepted',
587 'Request accepted, processing continues off-line'),
588 203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
589 204: ('No Content', 'Request fulfilled, nothing follows'),
590 205: ('Reset Content', 'Clear input form for further input.'),
591 206: ('Partial Content', 'Partial content follows.'),
592
593 300: ('Multiple Choices',
594 'Object has several resources -- see URI list'),
595 301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
596 302: ('Found', 'Object moved temporarily -- see URI list'),
597 303: ('See Other', 'Object moved -- see Method and URL list'),
598 304: ('Not Modified',
599 'Document has not changed since given time'),
600 305: ('Use Proxy',
601 'You must use proxy specified in Location to access this '
602 'resource.'),
603 307: ('Temporary Redirect',
604 'Object moved temporarily -- see URI list'),
605
606 400: ('Bad Request',
607 'Bad request syntax or unsupported method'),
608 401: ('Unauthorized',
609 'No permission -- see authorization schemes'),
610 402: ('Payment Required',
611 'No payment -- see charging schemes'),
612 403: ('Forbidden',
613 'Request forbidden -- authorization will not help'),
614 404: ('Not Found', 'Nothing matches the given URI'),
615 405: ('Method Not Allowed',
Senthil Kumaran7aa26212010-02-22 11:00:50 +0000616 'Specified method is invalid for this resource.'),
Georg Brandl24420152008-05-26 16:32:26 +0000617 406: ('Not Acceptable', 'URI not available in preferred format.'),
618 407: ('Proxy Authentication Required', 'You must authenticate with '
619 'this proxy before proceeding.'),
620 408: ('Request Timeout', 'Request timed out; try again later.'),
621 409: ('Conflict', 'Request conflict.'),
622 410: ('Gone',
623 'URI no longer exists and has been permanently removed.'),
624 411: ('Length Required', 'Client must specify Content-Length.'),
625 412: ('Precondition Failed', 'Precondition in headers is false.'),
626 413: ('Request Entity Too Large', 'Entity is too large.'),
627 414: ('Request-URI Too Long', 'URI is too long.'),
628 415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
629 416: ('Requested Range Not Satisfiable',
630 'Cannot satisfy request range.'),
631 417: ('Expectation Failed',
632 'Expect condition could not be satisfied.'),
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200633 428: ('Precondition Required',
634 'The origin server requires the request to be conditional.'),
635 429: ('Too Many Requests', 'The user has sent too many requests '
636 'in a given amount of time ("rate limiting").'),
637 431: ('Request Header Fields Too Large', 'The server is unwilling to '
638 'process the request because its header fields are too large.'),
Georg Brandl24420152008-05-26 16:32:26 +0000639
640 500: ('Internal Server Error', 'Server got itself in trouble'),
641 501: ('Not Implemented',
642 'Server does not support this operation'),
643 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
644 503: ('Service Unavailable',
645 'The server cannot process the request due to a high load'),
646 504: ('Gateway Timeout',
647 'The gateway server did not receive a timely response'),
648 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200649 511: ('Network Authentication Required',
650 'The client needs to authenticate to gain network access.'),
Georg Brandl24420152008-05-26 16:32:26 +0000651 }
652
653
654class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
655
656 """Simple HTTP request handler with GET and HEAD commands.
657
658 This serves files from the current directory and any of its
659 subdirectories. The MIME type for files is determined by
660 calling the .guess_type() method.
661
662 The GET and HEAD requests are identical except that the HEAD
663 request omits the actual contents of the file.
664
665 """
666
667 server_version = "SimpleHTTP/" + __version__
668
669 def do_GET(self):
670 """Serve a GET request."""
671 f = self.send_head()
672 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200673 try:
674 self.copyfile(f, self.wfile)
675 finally:
676 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000677
678 def do_HEAD(self):
679 """Serve a HEAD request."""
680 f = self.send_head()
681 if f:
682 f.close()
683
684 def send_head(self):
685 """Common code for GET and HEAD commands.
686
687 This sends the response code and MIME headers.
688
689 Return value is either a file object (which has to be copied
690 to the outputfile by the caller unless the command was HEAD,
691 and must be closed by the caller under all circumstances), or
692 None, in which case the caller has nothing further to do.
693
694 """
695 path = self.translate_path(self.path)
696 f = None
697 if os.path.isdir(path):
698 if not self.path.endswith('/'):
699 # redirect browser - doing basically what apache does
700 self.send_response(301)
701 self.send_header("Location", self.path + "/")
702 self.end_headers()
703 return None
704 for index in "index.html", "index.htm":
705 index = os.path.join(path, index)
706 if os.path.exists(index):
707 path = index
708 break
709 else:
710 return self.list_directory(path)
711 ctype = self.guess_type(path)
712 try:
713 f = open(path, 'rb')
714 except IOError:
715 self.send_error(404, "File not found")
716 return None
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200717 try:
718 self.send_response(200)
719 self.send_header("Content-type", ctype)
720 fs = os.fstat(f.fileno())
721 self.send_header("Content-Length", str(fs[6]))
722 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
723 self.end_headers()
724 return f
725 except:
726 f.close()
727 raise
Georg Brandl24420152008-05-26 16:32:26 +0000728
729 def list_directory(self, path):
730 """Helper to produce a directory listing (absent index.html).
731
732 Return value is either a file object, or None (indicating an
733 error). In either case, the headers are sent, making the
734 interface the same as for send_head().
735
736 """
737 try:
738 list = os.listdir(path)
739 except os.error:
740 self.send_error(404, "No permission to list directory")
741 return None
742 list.sort(key=lambda a: a.lower())
743 r = []
Georg Brandl1f7fffb2010-10-15 15:57:45 +0000744 displaypath = html.escape(urllib.parse.unquote(self.path))
Ezio Melottica897e92011-11-02 19:33:29 +0200745 enc = sys.getfilesystemencoding()
746 title = 'Directory listing for %s' % displaypath
747 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
748 '"http://www.w3.org/TR/html4/strict.dtd">')
749 r.append('<html>\n<head>')
750 r.append('<meta http-equiv="Content-Type" '
751 'content="text/html; charset=%s">' % enc)
752 r.append('<title>%s</title>\n</head>' % title)
753 r.append('<body>\n<h1>%s</h1>' % title)
754 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000755 for name in list:
756 fullname = os.path.join(path, name)
757 displayname = linkname = name
758 # Append / for directories or @ for symbolic links
759 if os.path.isdir(fullname):
760 displayname = name + "/"
761 linkname = name + "/"
762 if os.path.islink(fullname):
763 displayname = name + "@"
764 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200765 r.append('<li><a href="%s">%s</a></li>'
Georg Brandl1f7fffb2010-10-15 15:57:45 +0000766 % (urllib.parse.quote(linkname), html.escape(displayname)))
Ezio Melottica897e92011-11-02 19:33:29 +0200767 r.append('</ul>\n<hr>\n</body>\n</html>\n')
768 encoded = '\n'.join(r).encode(enc)
Georg Brandl24420152008-05-26 16:32:26 +0000769 f = io.BytesIO()
770 f.write(encoded)
771 f.seek(0)
772 self.send_response(200)
773 self.send_header("Content-type", "text/html; charset=%s" % enc)
774 self.send_header("Content-Length", str(len(encoded)))
775 self.end_headers()
776 return f
777
778 def translate_path(self, path):
779 """Translate a /-separated PATH to the local filename syntax.
780
781 Components that mean special things to the local file system
782 (e.g. drive or directory names) are ignored. (XXX They should
783 probably be diagnosed.)
784
785 """
786 # abandon query parameters
787 path = path.split('?',1)[0]
788 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700789 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700790 trailing_slash = path.rstrip().endswith('/')
Jeremy Hylton1afc1692008-06-18 20:49:58 +0000791 path = posixpath.normpath(urllib.parse.unquote(path))
Georg Brandl24420152008-05-26 16:32:26 +0000792 words = path.split('/')
793 words = filter(None, words)
794 path = os.getcwd()
795 for word in words:
796 drive, word = os.path.splitdrive(word)
797 head, word = os.path.split(word)
798 if word in (os.curdir, os.pardir): continue
799 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700800 if trailing_slash:
801 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000802 return path
803
804 def copyfile(self, source, outputfile):
805 """Copy all data between two file objects.
806
807 The SOURCE argument is a file object open for reading
808 (or anything with a read() method) and the DESTINATION
809 argument is a file object open for writing (or
810 anything with a write() method).
811
812 The only reason for overriding this would be to change
813 the block size or perhaps to replace newlines by CRLF
814 -- note however that this the default server uses this
815 to copy binary data as well.
816
817 """
818 shutil.copyfileobj(source, outputfile)
819
820 def guess_type(self, path):
821 """Guess the type of a file.
822
823 Argument is a PATH (a filename).
824
825 Return value is a string of the form type/subtype,
826 usable for a MIME Content-type header.
827
828 The default implementation looks the file's extension
829 up in the table self.extensions_map, using application/octet-stream
830 as a default; however it would be permissible (if
831 slow) to look inside the data to make a better guess.
832
833 """
834
835 base, ext = posixpath.splitext(path)
836 if ext in self.extensions_map:
837 return self.extensions_map[ext]
838 ext = ext.lower()
839 if ext in self.extensions_map:
840 return self.extensions_map[ext]
841 else:
842 return self.extensions_map['']
843
844 if not mimetypes.inited:
845 mimetypes.init() # try to read system mime.types
846 extensions_map = mimetypes.types_map.copy()
847 extensions_map.update({
848 '': 'application/octet-stream', # Default
849 '.py': 'text/plain',
850 '.c': 'text/plain',
851 '.h': 'text/plain',
852 })
853
854
855# Utilities for CGIHTTPRequestHandler
856
Senthil Kumarand70846b2012-04-12 02:34:32 +0800857def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000858 """
859 Given a URL path, remove extra '/'s and '.' path elements and collapse
Senthil Kumarand70846b2012-04-12 02:34:32 +0800860 any '..' references and returns a colllapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000861
862 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800863 The utility of this function is limited to is_cgi method and helps
864 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000865
866 Returns: A tuple of (head, tail) where tail is everything after the final /
867 and head is everything before it. Head will always start with a '/' and,
868 if it contains anything else, never have a trailing '/'.
869
870 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800871
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000872 """
873 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
874 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800875 path_parts = path.split('/')
876 head_parts = []
877 for part in path_parts[:-1]:
878 if part == '..':
879 head_parts.pop() # IndexError if more '..' than prior parts
880 elif part and part != '.':
881 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000882 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800883 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800884 if tail_part:
885 if tail_part == '..':
886 head_parts.pop()
887 tail_part = ''
888 elif tail_part == '.':
889 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000890 else:
891 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800892
893 splitpath = ('/' + '/'.join(head_parts), tail_part)
894 collapsed_path = "/".join(splitpath)
895
896 return collapsed_path
897
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000898
899
Georg Brandl24420152008-05-26 16:32:26 +0000900nobody = None
901
902def nobody_uid():
903 """Internal routine to get nobody's uid"""
904 global nobody
905 if nobody:
906 return nobody
907 try:
908 import pwd
909 except ImportError:
910 return -1
911 try:
912 nobody = pwd.getpwnam('nobody')[2]
913 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000914 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000915 return nobody
916
917
918def executable(path):
919 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200920 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000921
922
923class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
924
925 """Complete HTTP server with GET, HEAD and POST commands.
926
927 GET and HEAD also support running CGI scripts.
928
929 The POST command is *only* implemented for CGI scripts.
930
931 """
932
933 # Determine platform specifics
934 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000935
936 # Make rfile unbuffered -- we need to read one line and then pass
937 # the rest to a subprocess, so we can't use buffered input.
938 rbufsize = 0
939
940 def do_POST(self):
941 """Serve a POST request.
942
943 This is only implemented for CGI scripts.
944
945 """
946
947 if self.is_cgi():
948 self.run_cgi()
949 else:
950 self.send_error(501, "Can only POST to CGI scripts")
951
952 def send_head(self):
953 """Version of send_head that support CGI scripts"""
954 if self.is_cgi():
955 return self.run_cgi()
956 else:
957 return SimpleHTTPRequestHandler.send_head(self)
958
959 def is_cgi(self):
960 """Test whether self.path corresponds to a CGI script.
961
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000962 Returns True and updates the cgi_info attribute to the tuple
963 (dir, rest) if self.path requires running a CGI script.
964 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000965
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000966 If any exception is raised, the caller should assume that
967 self.path was rejected as invalid and act accordingly.
968
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000969 The default implementation tests whether the normalized url
970 path begins with one of the strings in self.cgi_directories
971 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000972
973 """
Senthil Kumarand70846b2012-04-12 02:34:32 +0800974 collapsed_path = _url_collapse_path(self.path)
975 dir_sep = collapsed_path.find('/', 1)
976 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800977 if head in self.cgi_directories:
978 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000979 return True
Georg Brandl24420152008-05-26 16:32:26 +0000980 return False
981
Senthil Kumarand70846b2012-04-12 02:34:32 +0800982
Georg Brandl24420152008-05-26 16:32:26 +0000983 cgi_directories = ['/cgi-bin', '/htbin']
984
985 def is_executable(self, path):
986 """Test whether argument path is an executable file."""
987 return executable(path)
988
989 def is_python(self, path):
990 """Test whether argument path is a Python script."""
991 head, tail = os.path.splitext(path)
992 return tail.lower() in (".py", ".pyw")
993
994 def run_cgi(self):
995 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +0000996 dir, rest = self.cgi_info
997
Benjamin Peterson04e9de42013-10-30 12:43:09 -0400998 i = rest.find('/')
Georg Brandl24420152008-05-26 16:32:26 +0000999 while i >= 0:
Benjamin Peterson04e9de42013-10-30 12:43:09 -04001000 nextdir = rest[:i]
1001 nextrest = rest[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +00001002
1003 scriptdir = self.translate_path(nextdir)
1004 if os.path.isdir(scriptdir):
1005 dir, rest = nextdir, nextrest
Benjamin Peterson04e9de42013-10-30 12:43:09 -04001006 i = rest.find('/')
Georg Brandl24420152008-05-26 16:32:26 +00001007 else:
1008 break
1009
1010 # find an explicit query string, if present.
1011 i = rest.rfind('?')
1012 if i >= 0:
1013 rest, query = rest[:i], rest[i+1:]
1014 else:
1015 query = ''
1016
1017 # dissect the part after the directory name into a script name &
1018 # a possible additional path, to be stored in PATH_INFO.
1019 i = rest.find('/')
1020 if i >= 0:
1021 script, rest = rest[:i], rest[i:]
1022 else:
1023 script, rest = rest, ''
1024
1025 scriptname = dir + '/' + script
1026 scriptfile = self.translate_path(scriptname)
1027 if not os.path.exists(scriptfile):
1028 self.send_error(404, "No such CGI script (%r)" % scriptname)
1029 return
1030 if not os.path.isfile(scriptfile):
1031 self.send_error(403, "CGI script is not a plain file (%r)" %
1032 scriptname)
1033 return
1034 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001035 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001036 if not self.is_executable(scriptfile):
1037 self.send_error(403, "CGI script is not executable (%r)" %
1038 scriptname)
1039 return
1040
1041 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1042 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001043 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001044 env['SERVER_SOFTWARE'] = self.version_string()
1045 env['SERVER_NAME'] = self.server.server_name
1046 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1047 env['SERVER_PROTOCOL'] = self.protocol_version
1048 env['SERVER_PORT'] = str(self.server.server_port)
1049 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001050 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001051 env['PATH_INFO'] = uqrest
1052 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1053 env['SCRIPT_NAME'] = scriptname
1054 if query:
1055 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001056 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001057 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001058 if authorization:
1059 authorization = authorization.split()
1060 if len(authorization) == 2:
1061 import base64, binascii
1062 env['AUTH_TYPE'] = authorization[0]
1063 if authorization[0].lower() == "basic":
1064 try:
1065 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001066 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001067 decode('ascii')
1068 except (binascii.Error, UnicodeError):
1069 pass
1070 else:
1071 authorization = authorization.split(':')
1072 if len(authorization) == 2:
1073 env['REMOTE_USER'] = authorization[0]
1074 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001075 if self.headers.get('content-type') is None:
1076 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001077 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001078 env['CONTENT_TYPE'] = self.headers['content-type']
1079 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001080 if length:
1081 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001082 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001083 if referer:
1084 env['HTTP_REFERER'] = referer
1085 accept = []
1086 for line in self.headers.getallmatchingheaders('accept'):
1087 if line[:1] in "\t\n\r ":
1088 accept.append(line.strip())
1089 else:
1090 accept = accept + line[7:].split(',')
1091 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001092 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001093 if ua:
1094 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001095 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001096 cookie_str = ', '.join(co)
1097 if cookie_str:
1098 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001099 # XXX Other HTTP_* headers
1100 # Since we're setting the env in the parent, provide empty
1101 # values to override previously set values
1102 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1103 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1104 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001105
1106 self.send_response(200, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001107 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001108
1109 decoded_query = query.replace('+', ' ')
1110
1111 if self.have_fork:
1112 # Unix -- fork as we should
1113 args = [script]
1114 if '=' not in decoded_query:
1115 args.append(decoded_query)
1116 nobody = nobody_uid()
1117 self.wfile.flush() # Always flush before forking
1118 pid = os.fork()
1119 if pid != 0:
1120 # Parent
1121 pid, sts = os.waitpid(pid, 0)
1122 # throw away additional data [see bug #427345]
1123 while select.select([self.rfile], [], [], 0)[0]:
1124 if not self.rfile.read(1):
1125 break
1126 if sts:
1127 self.log_error("CGI script exit status %#x", sts)
1128 return
1129 # Child
1130 try:
1131 try:
1132 os.setuid(nobody)
1133 except os.error:
1134 pass
1135 os.dup2(self.rfile.fileno(), 0)
1136 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001137 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001138 except:
1139 self.server.handle_error(self.request, self.client_address)
1140 os._exit(127)
1141
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001142 else:
1143 # Non-Unix -- use subprocess
1144 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001145 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001146 if self.is_python(scriptfile):
1147 interp = sys.executable
1148 if interp.lower().endswith("w.exe"):
1149 # On Windows, use python.exe, not pythonw.exe
1150 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001151 cmdline = [interp, '-u'] + cmdline
1152 if '=' not in query:
1153 cmdline.append(query)
1154 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001155 try:
1156 nbytes = int(length)
1157 except (TypeError, ValueError):
1158 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001159 p = subprocess.Popen(cmdline,
1160 stdin=subprocess.PIPE,
1161 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001162 stderr=subprocess.PIPE,
1163 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001164 )
Georg Brandl24420152008-05-26 16:32:26 +00001165 if self.command.lower() == "post" and nbytes > 0:
1166 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001167 else:
1168 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001169 # throw away additional data [see bug #427345]
1170 while select.select([self.rfile._sock], [], [], 0)[0]:
1171 if not self.rfile._sock.recv(1):
1172 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001173 stdout, stderr = p.communicate(data)
1174 self.wfile.write(stdout)
1175 if stderr:
1176 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001177 p.stderr.close()
1178 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001179 status = p.returncode
1180 if status:
1181 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001182 else:
1183 self.log_message("CGI script exited OK")
1184
1185
1186def test(HandlerClass = BaseHTTPRequestHandler,
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001187 ServerClass = HTTPServer, protocol="HTTP/1.0", port=8000):
Georg Brandl24420152008-05-26 16:32:26 +00001188 """Test the HTTP request handler class.
1189
1190 This runs an HTTP server on port 8000 (or the first command line
1191 argument).
1192
1193 """
Georg Brandl24420152008-05-26 16:32:26 +00001194 server_address = ('', port)
1195
1196 HandlerClass.protocol_version = protocol
1197 httpd = ServerClass(server_address, HandlerClass)
1198
1199 sa = httpd.socket.getsockname()
1200 print("Serving HTTP on", sa[0], "port", sa[1], "...")
Alexandre Vassalottib5292a22009-04-03 07:16:55 +00001201 try:
1202 httpd.serve_forever()
1203 except KeyboardInterrupt:
1204 print("\nKeyboard interrupt received, exiting.")
1205 httpd.server_close()
1206 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001207
1208if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001209 parser = argparse.ArgumentParser()
1210 parser.add_argument('--cgi', action='store_true',
1211 help='Run as CGI Server')
1212 parser.add_argument('port', action='store',
1213 default=8000, type=int,
1214 nargs='?',
1215 help='Specify alternate port [default: 8000]')
1216 args = parser.parse_args()
1217 if args.cgi:
1218 test(HandlerClass=CGIHTTPRequestHandler, port=args.port)
1219 else:
1220 test(HandlerClass=SimpleHTTPRequestHandler, port=args.port)