blob: cfa29f44d351cfd3ac6e8ea05d18b6d43c148e2b [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
Jeremy Hylton914ab452009-03-27 17:16:06 +000088import http.client
89import io
90import mimetypes
91import os
92import posixpath
93import select
94import shutil
95import socket # For gethostbyaddr()
96import socketserver
97import sys
98import time
99import urllib.parse
Senthil Kumaran42713722010-10-03 17:55:45 +0000100import copy
Senthil Kumaran1251faf2012-06-03 16:15:54 +0800101import argparse
102
Georg Brandl24420152008-05-26 16:32:26 +0000103
104# Default error message template
105DEFAULT_ERROR_MESSAGE = """\
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800106<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
107 "http://www.w3.org/TR/html4/strict.dtd">
Ezio Melottica897e92011-11-02 19:33:29 +0200108<html>
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800109 <head>
Senthil Kumaran1b407fe2011-03-20 10:44:30 +0800110 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
Senthil Kumaranb253c9f2011-03-17 16:43:22 +0800111 <title>Error response</title>
112 </head>
113 <body>
114 <h1>Error response</h1>
115 <p>Error code: %(code)d</p>
116 <p>Message: %(message)s.</p>
117 <p>Error code explanation: %(code)s - %(explain)s.</p>
118 </body>
119</html>
Georg Brandl24420152008-05-26 16:32:26 +0000120"""
121
122DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8"
123
124def _quote_html(html):
125 return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
126
127class HTTPServer(socketserver.TCPServer):
128
129 allow_reuse_address = 1 # Seems to make sense in testing environment
130
131 def server_bind(self):
132 """Override server_bind to store the server name."""
133 socketserver.TCPServer.server_bind(self)
134 host, port = self.socket.getsockname()[:2]
135 self.server_name = socket.getfqdn(host)
136 self.server_port = port
137
138
139class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
140
141 """HTTP request handler base class.
142
143 The following explanation of HTTP serves to guide you through the
144 code as well as to expose any misunderstandings I may have about
145 HTTP (so you don't need to read the code to figure out I'm wrong
146 :-).
147
148 HTTP (HyperText Transfer Protocol) is an extensible protocol on
149 top of a reliable stream transport (e.g. TCP/IP). The protocol
150 recognizes three parts to a request:
151
152 1. One line identifying the request type and path
153 2. An optional set of RFC-822-style headers
154 3. An optional data part
155
156 The headers and data are separated by a blank line.
157
158 The first line of the request has the form
159
160 <command> <path> <version>
161
162 where <command> is a (case-sensitive) keyword such as GET or POST,
163 <path> is a string containing path information for the request,
164 and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
165 <path> is encoded using the URL encoding scheme (using %xx to signify
166 the ASCII character with hex code xx).
167
168 The specification specifies that lines are separated by CRLF but
169 for compatibility with the widest range of clients recommends
170 servers also handle LF. Similarly, whitespace in the request line
171 is treated sensibly (allowing multiple spaces between components
172 and allowing trailing whitespace).
173
174 Similarly, for output, lines ought to be separated by CRLF pairs
175 but most clients grok LF characters just fine.
176
177 If the first line of the request has the form
178
179 <command> <path>
180
181 (i.e. <version> is left out) then this is assumed to be an HTTP
182 0.9 request; this form has no optional headers and data part and
183 the reply consists of just the data.
184
185 The reply form of the HTTP 1.x protocol again has three parts:
186
187 1. One line giving the response code
188 2. An optional set of RFC-822-style headers
189 3. The data
190
191 Again, the headers and data are separated by a blank line.
192
193 The response code line has the form
194
195 <version> <responsecode> <responsestring>
196
197 where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
198 <responsecode> is a 3-digit response code indicating success or
199 failure of the request, and <responsestring> is an optional
200 human-readable string explaining what the response code means.
201
202 This server parses the request and the headers, and then calls a
203 function specific to the request type (<command>). Specifically,
204 a request SPAM will be handled by a method do_SPAM(). If no
205 such method exists the server sends an error response to the
206 client. If it exists, it is called with no arguments:
207
208 do_SPAM()
209
210 Note that the request name is case sensitive (i.e. SPAM and spam
211 are different requests).
212
213 The various request details are stored in instance variables:
214
215 - client_address is the client IP address in the form (host,
216 port);
217
218 - command, path and version are the broken-down request line;
219
Barry Warsaw820c1202008-06-12 04:06:45 +0000220 - headers is an instance of email.message.Message (or a derived
Georg Brandl24420152008-05-26 16:32:26 +0000221 class) containing the header information;
222
223 - rfile is a file object open for reading positioned at the
224 start of the optional input data part;
225
226 - wfile is a file object open for writing.
227
228 IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
229
230 The first thing to be written must be the response line. Then
231 follow 0 or more header lines, then a blank line, and then the
232 actual data (if any). The meaning of the header lines depends on
233 the command executed by the server; in most cases, when data is
234 returned, there should be at least one header line of the form
235
236 Content-type: <type>/<subtype>
237
238 where <type> and <subtype> should be registered MIME types,
239 e.g. "text/html" or "text/plain".
240
241 """
242
243 # The Python system version, truncated to its first component.
244 sys_version = "Python/" + sys.version.split()[0]
245
246 # The server software version. You may want to override this.
247 # The format is multiple whitespace-separated strings,
248 # where each string is of the form name[/version].
249 server_version = "BaseHTTP/" + __version__
250
251 error_message_format = DEFAULT_ERROR_MESSAGE
252 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
253
254 # The default request version. This only affects responses up until
255 # the point where the request line is parsed, so it mainly decides what
256 # the client gets back when sending a malformed request line.
257 # Most web servers default to HTTP 0.9, i.e. don't send a status line.
258 default_request_version = "HTTP/0.9"
259
260 def parse_request(self):
261 """Parse a request (internal).
262
263 The request should be stored in self.raw_requestline; the results
264 are in self.command, self.path, self.request_version and
265 self.headers.
266
267 Return True for success, False for failure; on failure, an
268 error is sent back.
269
270 """
271 self.command = None # set in case of error on the first line
272 self.request_version = version = self.default_request_version
273 self.close_connection = 1
274 requestline = str(self.raw_requestline, 'iso-8859-1')
Senthil Kumaran30755492011-12-23 17:03:41 +0800275 requestline = requestline.rstrip('\r\n')
Georg Brandl24420152008-05-26 16:32:26 +0000276 self.requestline = requestline
277 words = requestline.split()
278 if len(words) == 3:
Senthil Kumaran30755492011-12-23 17:03:41 +0800279 command, path, version = words
Georg Brandl24420152008-05-26 16:32:26 +0000280 if version[:5] != 'HTTP/':
281 self.send_error(400, "Bad request version (%r)" % version)
282 return False
283 try:
284 base_version_number = version.split('/', 1)[1]
285 version_number = base_version_number.split(".")
286 # RFC 2145 section 3.1 says there can be only one "." and
287 # - major and minor numbers MUST be treated as
288 # separate integers;
289 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
290 # turn is lower than HTTP/12.3;
291 # - Leading zeros MUST be ignored by recipients.
292 if len(version_number) != 2:
293 raise ValueError
294 version_number = int(version_number[0]), int(version_number[1])
295 except (ValueError, IndexError):
296 self.send_error(400, "Bad request version (%r)" % version)
297 return False
298 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
299 self.close_connection = 0
300 if version_number >= (2, 0):
301 self.send_error(505,
302 "Invalid HTTP Version (%s)" % base_version_number)
303 return False
304 elif len(words) == 2:
Senthil Kumaran30755492011-12-23 17:03:41 +0800305 command, path = words
Georg Brandl24420152008-05-26 16:32:26 +0000306 self.close_connection = 1
307 if command != 'GET':
308 self.send_error(400,
309 "Bad HTTP/0.9 request type (%r)" % command)
310 return False
311 elif not words:
312 return False
313 else:
314 self.send_error(400, "Bad request syntax (%r)" % requestline)
315 return False
316 self.command, self.path, self.request_version = command, path, version
317
318 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000319 try:
320 self.headers = http.client.parse_headers(self.rfile,
321 _class=self.MessageClass)
322 except http.client.LineTooLong:
323 self.send_error(400, "Line too long")
324 return False
Georg Brandl24420152008-05-26 16:32:26 +0000325
326 conntype = self.headers.get('Connection', "")
327 if conntype.lower() == 'close':
328 self.close_connection = 1
329 elif (conntype.lower() == 'keep-alive' and
330 self.protocol_version >= "HTTP/1.1"):
331 self.close_connection = 0
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000332 # Examine the headers and look for an Expect directive
333 expect = self.headers.get('Expect', "")
334 if (expect.lower() == "100-continue" and
335 self.protocol_version >= "HTTP/1.1" and
336 self.request_version >= "HTTP/1.1"):
337 if not self.handle_expect_100():
338 return False
339 return True
340
341 def handle_expect_100(self):
342 """Decide what to do with an "Expect: 100-continue" header.
343
344 If the client is expecting a 100 Continue response, we must
345 respond with either a 100 Continue or a final response before
346 waiting for the request body. The default is to always respond
347 with a 100 Continue. You can behave differently (for example,
348 reject unauthorized requests) by overriding this method.
349
350 This method should either return True (possibly after sending
351 a 100 Continue response) or send an error response and return
352 False.
353
354 """
355 self.send_response_only(100)
Benjamin Peterson04424232014-01-18 21:50:18 -0500356 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000357 return True
358
359 def handle_one_request(self):
360 """Handle a single HTTP request.
361
362 You normally don't need to override this method; see the class
363 __doc__ string for information on how to handle specific HTTP
364 commands such as GET and POST.
365
366 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000367 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000368 self.raw_requestline = self.rfile.readline(65537)
369 if len(self.raw_requestline) > 65536:
370 self.requestline = ''
371 self.request_version = ''
372 self.command = ''
373 self.send_error(414)
374 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000375 if not self.raw_requestline:
376 self.close_connection = 1
377 return
378 if not self.parse_request():
379 # An error code has been sent, just exit
380 return
381 mname = 'do_' + self.command
382 if not hasattr(self, mname):
383 self.send_error(501, "Unsupported method (%r)" % self.command)
384 return
385 method = getattr(self, mname)
386 method()
387 self.wfile.flush() #actually send the response if not already done.
388 except socket.timeout as e:
389 #a read or a write timed out. Discard this connection
390 self.log_error("Request timed out: %r", e)
Georg Brandl24420152008-05-26 16:32:26 +0000391 self.close_connection = 1
392 return
Georg Brandl24420152008-05-26 16:32:26 +0000393
394 def handle(self):
395 """Handle multiple requests if necessary."""
396 self.close_connection = 1
397
398 self.handle_one_request()
399 while not self.close_connection:
400 self.handle_one_request()
401
Senthil Kumaran26886442013-03-15 07:53:21 -0700402 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000403 """Send and log an error reply.
404
Senthil Kumaran26886442013-03-15 07:53:21 -0700405 Arguments are
406 * code: an HTTP error code
407 3 digits
408 * message: a simple optional 1 line reason phrase.
409 *( HTAB / SP / VCHAR / %x80-FF )
410 defaults to short entry matching the response code
411 * explain: a detailed message defaults to the long entry
412 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000413
414 This sends an error response (so it must be called before any
415 output has been generated), logs the error, and finally sends
416 a piece of HTML explaining the error to the user.
417
418 """
419
420 try:
421 shortmsg, longmsg = self.responses[code]
422 except KeyError:
423 shortmsg, longmsg = '???', '???'
424 if message is None:
425 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700426 if explain is None:
427 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000428 self.log_error("code %d, message %s", code, message)
429 # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
430 content = (self.error_message_format %
Senthil Kumaran26886442013-03-15 07:53:21 -0700431 {'code': code, 'message': _quote_html(message), 'explain': _quote_html(explain)})
Senthil Kumaran52d27202012-10-10 23:16:21 -0700432 body = content.encode('UTF-8', 'replace')
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800433 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000434 self.send_header("Content-Type", self.error_content_type)
435 self.send_header('Connection', 'close')
Senthil Kumaran52d27202012-10-10 23:16:21 -0700436 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000437 self.end_headers()
438 if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
Senthil Kumaran52d27202012-10-10 23:16:21 -0700439 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000440
441 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800442 """Add the response header to the headers buffer and log the
443 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000444
445 Also send two standard headers with the server software
446 version and the current date.
447
448 """
449 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000450 self.send_response_only(code, message)
451 self.send_header('Server', self.version_string())
452 self.send_header('Date', self.date_time_string())
453
454 def send_response_only(self, code, message=None):
455 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000456 if message is None:
457 if code in self.responses:
458 message = self.responses[code][0]
459 else:
460 message = ''
461 if self.request_version != 'HTTP/0.9':
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800462 if not hasattr(self, '_headers_buffer'):
463 self._headers_buffer = []
464 self._headers_buffer.append(("%s %d %s\r\n" %
465 (self.protocol_version, code, message)).encode(
466 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000467
468 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800469 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000470 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000471 if not hasattr(self, '_headers_buffer'):
472 self._headers_buffer = []
473 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000474 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000475
476 if keyword.lower() == 'connection':
477 if value.lower() == 'close':
478 self.close_connection = 1
479 elif value.lower() == 'keep-alive':
480 self.close_connection = 0
481
482 def end_headers(self):
483 """Send the blank line ending the MIME headers."""
484 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000485 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800486 self.flush_headers()
487
488 def flush_headers(self):
489 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000490 self.wfile.write(b"".join(self._headers_buffer))
491 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000492
493 def log_request(self, code='-', size='-'):
494 """Log an accepted request.
495
496 This is called by send_response().
497
498 """
499
500 self.log_message('"%s" %s %s',
501 self.requestline, str(code), str(size))
502
503 def log_error(self, format, *args):
504 """Log an error.
505
506 This is called when a request cannot be fulfilled. By
507 default it passes the message on to log_message().
508
509 Arguments are the same as for log_message().
510
511 XXX This should go to the separate error log.
512
513 """
514
515 self.log_message(format, *args)
516
517 def log_message(self, format, *args):
518 """Log an arbitrary message.
519
520 This is used by all other logging functions. Override
521 it if you have specific logging wishes.
522
523 The first argument, FORMAT, is a format string for the
524 message to be logged. If the format string contains
525 any % escapes requiring parameters, they should be
526 specified as subsequent arguments (it's just like
527 printf!).
528
Senthil Kumarandb727b42012-04-29 13:41:03 +0800529 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000530 every message.
531
532 """
533
534 sys.stderr.write("%s - - [%s] %s\n" %
535 (self.address_string(),
536 self.log_date_time_string(),
537 format%args))
538
539 def version_string(self):
540 """Return the server software version string."""
541 return self.server_version + ' ' + self.sys_version
542
543 def date_time_string(self, timestamp=None):
544 """Return the current date and time formatted for a message header."""
545 if timestamp is None:
546 timestamp = time.time()
547 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
548 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
549 self.weekdayname[wd],
550 day, self.monthname[month], year,
551 hh, mm, ss)
552 return s
553
554 def log_date_time_string(self):
555 """Return the current time formatted for logging."""
556 now = time.time()
557 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
558 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
559 day, self.monthname[month], year, hh, mm, ss)
560 return s
561
562 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
563
564 monthname = [None,
565 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
566 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
567
568 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800569 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000570
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800571 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000572
573 # Essentially static class variables
574
575 # The version of the HTTP protocol we support.
576 # Set this to HTTP/1.1 to enable automatic keepalive
577 protocol_version = "HTTP/1.0"
578
Barry Warsaw820c1202008-06-12 04:06:45 +0000579 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000580 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000581
582 # Table mapping response codes to messages; entries have the
583 # form {code: (shortmessage, longmessage)}.
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200584 # See RFC 2616 and 6585.
Georg Brandl24420152008-05-26 16:32:26 +0000585 responses = {
586 100: ('Continue', 'Request received, please continue'),
587 101: ('Switching Protocols',
588 'Switching to new protocol; obey Upgrade header'),
589
590 200: ('OK', 'Request fulfilled, document follows'),
591 201: ('Created', 'Document created, URL follows'),
592 202: ('Accepted',
593 'Request accepted, processing continues off-line'),
594 203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
595 204: ('No Content', 'Request fulfilled, nothing follows'),
596 205: ('Reset Content', 'Clear input form for further input.'),
597 206: ('Partial Content', 'Partial content follows.'),
598
599 300: ('Multiple Choices',
600 'Object has several resources -- see URI list'),
601 301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
602 302: ('Found', 'Object moved temporarily -- see URI list'),
603 303: ('See Other', 'Object moved -- see Method and URL list'),
604 304: ('Not Modified',
605 'Document has not changed since given time'),
606 305: ('Use Proxy',
607 'You must use proxy specified in Location to access this '
608 'resource.'),
609 307: ('Temporary Redirect',
610 'Object moved temporarily -- see URI list'),
611
612 400: ('Bad Request',
613 'Bad request syntax or unsupported method'),
614 401: ('Unauthorized',
615 'No permission -- see authorization schemes'),
616 402: ('Payment Required',
617 'No payment -- see charging schemes'),
618 403: ('Forbidden',
619 'Request forbidden -- authorization will not help'),
620 404: ('Not Found', 'Nothing matches the given URI'),
621 405: ('Method Not Allowed',
Senthil Kumaran7aa26212010-02-22 11:00:50 +0000622 'Specified method is invalid for this resource.'),
Georg Brandl24420152008-05-26 16:32:26 +0000623 406: ('Not Acceptable', 'URI not available in preferred format.'),
624 407: ('Proxy Authentication Required', 'You must authenticate with '
625 'this proxy before proceeding.'),
626 408: ('Request Timeout', 'Request timed out; try again later.'),
627 409: ('Conflict', 'Request conflict.'),
628 410: ('Gone',
629 'URI no longer exists and has been permanently removed.'),
630 411: ('Length Required', 'Client must specify Content-Length.'),
631 412: ('Precondition Failed', 'Precondition in headers is false.'),
632 413: ('Request Entity Too Large', 'Entity is too large.'),
633 414: ('Request-URI Too Long', 'URI is too long.'),
634 415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
635 416: ('Requested Range Not Satisfiable',
636 'Cannot satisfy request range.'),
637 417: ('Expectation Failed',
638 'Expect condition could not be satisfied.'),
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200639 428: ('Precondition Required',
640 'The origin server requires the request to be conditional.'),
641 429: ('Too Many Requests', 'The user has sent too many requests '
642 'in a given amount of time ("rate limiting").'),
643 431: ('Request Header Fields Too Large', 'The server is unwilling to '
644 'process the request because its header fields are too large.'),
Georg Brandl24420152008-05-26 16:32:26 +0000645
646 500: ('Internal Server Error', 'Server got itself in trouble'),
647 501: ('Not Implemented',
648 'Server does not support this operation'),
649 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
650 503: ('Service Unavailable',
651 'The server cannot process the request due to a high load'),
652 504: ('Gateway Timeout',
653 'The gateway server did not receive a timely response'),
654 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
Hynek Schlawack51b2ed52012-05-16 09:51:07 +0200655 511: ('Network Authentication Required',
656 'The client needs to authenticate to gain network access.'),
Georg Brandl24420152008-05-26 16:32:26 +0000657 }
658
659
660class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
661
662 """Simple HTTP request handler with GET and HEAD commands.
663
664 This serves files from the current directory and any of its
665 subdirectories. The MIME type for files is determined by
666 calling the .guess_type() method.
667
668 The GET and HEAD requests are identical except that the HEAD
669 request omits the actual contents of the file.
670
671 """
672
673 server_version = "SimpleHTTP/" + __version__
674
675 def do_GET(self):
676 """Serve a GET request."""
677 f = self.send_head()
678 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200679 try:
680 self.copyfile(f, self.wfile)
681 finally:
682 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000683
684 def do_HEAD(self):
685 """Serve a HEAD request."""
686 f = self.send_head()
687 if f:
688 f.close()
689
690 def send_head(self):
691 """Common code for GET and HEAD commands.
692
693 This sends the response code and MIME headers.
694
695 Return value is either a file object (which has to be copied
696 to the outputfile by the caller unless the command was HEAD,
697 and must be closed by the caller under all circumstances), or
698 None, in which case the caller has nothing further to do.
699
700 """
701 path = self.translate_path(self.path)
702 f = None
703 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600704 parts = urllib.parse.urlsplit(self.path)
705 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000706 # redirect browser - doing basically what apache does
707 self.send_response(301)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600708 new_parts = (parts[0], parts[1], parts[2] + '/',
709 parts[3], parts[4])
710 new_url = urllib.parse.urlunsplit(new_parts)
711 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000712 self.end_headers()
713 return None
714 for index in "index.html", "index.htm":
715 index = os.path.join(path, index)
716 if os.path.exists(index):
717 path = index
718 break
719 else:
720 return self.list_directory(path)
721 ctype = self.guess_type(path)
722 try:
723 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200724 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +0000725 self.send_error(404, "File not found")
726 return None
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200727 try:
728 self.send_response(200)
729 self.send_header("Content-type", ctype)
730 fs = os.fstat(f.fileno())
731 self.send_header("Content-Length", str(fs[6]))
732 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
733 self.end_headers()
734 return f
735 except:
736 f.close()
737 raise
Georg Brandl24420152008-05-26 16:32:26 +0000738
739 def list_directory(self, path):
740 """Helper to produce a directory listing (absent index.html).
741
742 Return value is either a file object, or None (indicating an
743 error). In either case, the headers are sent, making the
744 interface the same as for send_head().
745
746 """
747 try:
748 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200749 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +0000750 self.send_error(404, "No permission to list directory")
751 return None
752 list.sort(key=lambda a: a.lower())
753 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300754 try:
755 displaypath = urllib.parse.unquote(self.path,
756 errors='surrogatepass')
757 except UnicodeDecodeError:
758 displaypath = urllib.parse.unquote(path)
759 displaypath = html.escape(displaypath)
Ezio Melottica897e92011-11-02 19:33:29 +0200760 enc = sys.getfilesystemencoding()
761 title = 'Directory listing for %s' % displaypath
762 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
763 '"http://www.w3.org/TR/html4/strict.dtd">')
764 r.append('<html>\n<head>')
765 r.append('<meta http-equiv="Content-Type" '
766 'content="text/html; charset=%s">' % enc)
767 r.append('<title>%s</title>\n</head>' % title)
768 r.append('<body>\n<h1>%s</h1>' % title)
769 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000770 for name in list:
771 fullname = os.path.join(path, name)
772 displayname = linkname = name
773 # Append / for directories or @ for symbolic links
774 if os.path.isdir(fullname):
775 displayname = name + "/"
776 linkname = name + "/"
777 if os.path.islink(fullname):
778 displayname = name + "@"
779 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200780 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300781 % (urllib.parse.quote(linkname,
782 errors='surrogatepass'),
783 html.escape(displayname)))
Ezio Melottica897e92011-11-02 19:33:29 +0200784 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300785 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000786 f = io.BytesIO()
787 f.write(encoded)
788 f.seek(0)
789 self.send_response(200)
790 self.send_header("Content-type", "text/html; charset=%s" % enc)
791 self.send_header("Content-Length", str(len(encoded)))
792 self.end_headers()
793 return f
794
795 def translate_path(self, path):
796 """Translate a /-separated PATH to the local filename syntax.
797
798 Components that mean special things to the local file system
799 (e.g. drive or directory names) are ignored. (XXX They should
800 probably be diagnosed.)
801
802 """
803 # abandon query parameters
804 path = path.split('?',1)[0]
805 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700806 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700807 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300808 try:
809 path = urllib.parse.unquote(path, errors='surrogatepass')
810 except UnicodeDecodeError:
811 path = urllib.parse.unquote(path)
812 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000813 words = path.split('/')
814 words = filter(None, words)
815 path = os.getcwd()
816 for word in words:
817 drive, word = os.path.splitdrive(word)
818 head, word = os.path.split(word)
819 if word in (os.curdir, os.pardir): continue
820 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700821 if trailing_slash:
822 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000823 return path
824
825 def copyfile(self, source, outputfile):
826 """Copy all data between two file objects.
827
828 The SOURCE argument is a file object open for reading
829 (or anything with a read() method) and the DESTINATION
830 argument is a file object open for writing (or
831 anything with a write() method).
832
833 The only reason for overriding this would be to change
834 the block size or perhaps to replace newlines by CRLF
835 -- note however that this the default server uses this
836 to copy binary data as well.
837
838 """
839 shutil.copyfileobj(source, outputfile)
840
841 def guess_type(self, path):
842 """Guess the type of a file.
843
844 Argument is a PATH (a filename).
845
846 Return value is a string of the form type/subtype,
847 usable for a MIME Content-type header.
848
849 The default implementation looks the file's extension
850 up in the table self.extensions_map, using application/octet-stream
851 as a default; however it would be permissible (if
852 slow) to look inside the data to make a better guess.
853
854 """
855
856 base, ext = posixpath.splitext(path)
857 if ext in self.extensions_map:
858 return self.extensions_map[ext]
859 ext = ext.lower()
860 if ext in self.extensions_map:
861 return self.extensions_map[ext]
862 else:
863 return self.extensions_map['']
864
865 if not mimetypes.inited:
866 mimetypes.init() # try to read system mime.types
867 extensions_map = mimetypes.types_map.copy()
868 extensions_map.update({
869 '': 'application/octet-stream', # Default
870 '.py': 'text/plain',
871 '.c': 'text/plain',
872 '.h': 'text/plain',
873 })
874
875
876# Utilities for CGIHTTPRequestHandler
877
Senthil Kumarand70846b2012-04-12 02:34:32 +0800878def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000879 """
880 Given a URL path, remove extra '/'s and '.' path elements and collapse
Senthil Kumarand70846b2012-04-12 02:34:32 +0800881 any '..' references and returns a colllapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000882
883 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800884 The utility of this function is limited to is_cgi method and helps
885 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000886
887 Returns: A tuple of (head, tail) where tail is everything after the final /
888 and head is everything before it. Head will always start with a '/' and,
889 if it contains anything else, never have a trailing '/'.
890
891 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800892
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000893 """
894 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
895 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800896 path_parts = path.split('/')
897 head_parts = []
898 for part in path_parts[:-1]:
899 if part == '..':
900 head_parts.pop() # IndexError if more '..' than prior parts
901 elif part and part != '.':
902 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000903 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800904 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800905 if tail_part:
906 if tail_part == '..':
907 head_parts.pop()
908 tail_part = ''
909 elif tail_part == '.':
910 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000911 else:
912 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800913
914 splitpath = ('/' + '/'.join(head_parts), tail_part)
915 collapsed_path = "/".join(splitpath)
916
917 return collapsed_path
918
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000919
920
Georg Brandl24420152008-05-26 16:32:26 +0000921nobody = None
922
923def nobody_uid():
924 """Internal routine to get nobody's uid"""
925 global nobody
926 if nobody:
927 return nobody
928 try:
929 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400930 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000931 return -1
932 try:
933 nobody = pwd.getpwnam('nobody')[2]
934 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000935 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000936 return nobody
937
938
939def executable(path):
940 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200941 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000942
943
944class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
945
946 """Complete HTTP server with GET, HEAD and POST commands.
947
948 GET and HEAD also support running CGI scripts.
949
950 The POST command is *only* implemented for CGI scripts.
951
952 """
953
954 # Determine platform specifics
955 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000956
957 # Make rfile unbuffered -- we need to read one line and then pass
958 # the rest to a subprocess, so we can't use buffered input.
959 rbufsize = 0
960
961 def do_POST(self):
962 """Serve a POST request.
963
964 This is only implemented for CGI scripts.
965
966 """
967
968 if self.is_cgi():
969 self.run_cgi()
970 else:
971 self.send_error(501, "Can only POST to CGI scripts")
972
973 def send_head(self):
974 """Version of send_head that support CGI scripts"""
975 if self.is_cgi():
976 return self.run_cgi()
977 else:
978 return SimpleHTTPRequestHandler.send_head(self)
979
980 def is_cgi(self):
981 """Test whether self.path corresponds to a CGI script.
982
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000983 Returns True and updates the cgi_info attribute to the tuple
984 (dir, rest) if self.path requires running a CGI script.
985 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000986
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000987 If any exception is raised, the caller should assume that
988 self.path was rejected as invalid and act accordingly.
989
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000990 The default implementation tests whether the normalized url
991 path begins with one of the strings in self.cgi_directories
992 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000993
994 """
Benjamin Peterson73b8b1c2014-06-14 18:36:29 -0700995 collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path))
Senthil Kumarand70846b2012-04-12 02:34:32 +0800996 dir_sep = collapsed_path.find('/', 1)
997 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800998 if head in self.cgi_directories:
999 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +00001000 return True
Georg Brandl24420152008-05-26 16:32:26 +00001001 return False
1002
Senthil Kumarand70846b2012-04-12 02:34:32 +08001003
Georg Brandl24420152008-05-26 16:32:26 +00001004 cgi_directories = ['/cgi-bin', '/htbin']
1005
1006 def is_executable(self, path):
1007 """Test whether argument path is an executable file."""
1008 return executable(path)
1009
1010 def is_python(self, path):
1011 """Test whether argument path is a Python script."""
1012 head, tail = os.path.splitext(path)
1013 return tail.lower() in (".py", ".pyw")
1014
1015 def run_cgi(self):
1016 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +00001017 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -07001018 path = dir + '/' + rest
1019 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001020 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -07001021 nextdir = path[:i]
1022 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +00001023
1024 scriptdir = self.translate_path(nextdir)
1025 if os.path.isdir(scriptdir):
1026 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -07001027 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +00001028 else:
1029 break
1030
1031 # find an explicit query string, if present.
1032 i = rest.rfind('?')
1033 if i >= 0:
1034 rest, query = rest[:i], rest[i+1:]
1035 else:
1036 query = ''
1037
1038 # dissect the part after the directory name into a script name &
1039 # a possible additional path, to be stored in PATH_INFO.
1040 i = rest.find('/')
1041 if i >= 0:
1042 script, rest = rest[:i], rest[i:]
1043 else:
1044 script, rest = rest, ''
1045
1046 scriptname = dir + '/' + script
1047 scriptfile = self.translate_path(scriptname)
1048 if not os.path.exists(scriptfile):
1049 self.send_error(404, "No such CGI script (%r)" % scriptname)
1050 return
1051 if not os.path.isfile(scriptfile):
1052 self.send_error(403, "CGI script is not a plain file (%r)" %
1053 scriptname)
1054 return
1055 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001056 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001057 if not self.is_executable(scriptfile):
1058 self.send_error(403, "CGI script is not executable (%r)" %
1059 scriptname)
1060 return
1061
1062 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1063 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001064 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001065 env['SERVER_SOFTWARE'] = self.version_string()
1066 env['SERVER_NAME'] = self.server.server_name
1067 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1068 env['SERVER_PROTOCOL'] = self.protocol_version
1069 env['SERVER_PORT'] = str(self.server.server_port)
1070 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001071 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001072 env['PATH_INFO'] = uqrest
1073 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1074 env['SCRIPT_NAME'] = scriptname
1075 if query:
1076 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001077 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001078 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001079 if authorization:
1080 authorization = authorization.split()
1081 if len(authorization) == 2:
1082 import base64, binascii
1083 env['AUTH_TYPE'] = authorization[0]
1084 if authorization[0].lower() == "basic":
1085 try:
1086 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001087 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001088 decode('ascii')
1089 except (binascii.Error, UnicodeError):
1090 pass
1091 else:
1092 authorization = authorization.split(':')
1093 if len(authorization) == 2:
1094 env['REMOTE_USER'] = authorization[0]
1095 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001096 if self.headers.get('content-type') is None:
1097 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001098 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001099 env['CONTENT_TYPE'] = self.headers['content-type']
1100 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001101 if length:
1102 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001103 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001104 if referer:
1105 env['HTTP_REFERER'] = referer
1106 accept = []
1107 for line in self.headers.getallmatchingheaders('accept'):
1108 if line[:1] in "\t\n\r ":
1109 accept.append(line.strip())
1110 else:
1111 accept = accept + line[7:].split(',')
1112 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001113 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001114 if ua:
1115 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001116 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001117 cookie_str = ', '.join(co)
1118 if cookie_str:
1119 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001120 # XXX Other HTTP_* headers
1121 # Since we're setting the env in the parent, provide empty
1122 # values to override previously set values
1123 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1124 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1125 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001126
1127 self.send_response(200, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001128 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001129
1130 decoded_query = query.replace('+', ' ')
1131
1132 if self.have_fork:
1133 # Unix -- fork as we should
1134 args = [script]
1135 if '=' not in decoded_query:
1136 args.append(decoded_query)
1137 nobody = nobody_uid()
1138 self.wfile.flush() # Always flush before forking
1139 pid = os.fork()
1140 if pid != 0:
1141 # Parent
1142 pid, sts = os.waitpid(pid, 0)
1143 # throw away additional data [see bug #427345]
1144 while select.select([self.rfile], [], [], 0)[0]:
1145 if not self.rfile.read(1):
1146 break
1147 if sts:
1148 self.log_error("CGI script exit status %#x", sts)
1149 return
1150 # Child
1151 try:
1152 try:
1153 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001154 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001155 pass
1156 os.dup2(self.rfile.fileno(), 0)
1157 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001158 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001159 except:
1160 self.server.handle_error(self.request, self.client_address)
1161 os._exit(127)
1162
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001163 else:
1164 # Non-Unix -- use subprocess
1165 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001166 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001167 if self.is_python(scriptfile):
1168 interp = sys.executable
1169 if interp.lower().endswith("w.exe"):
1170 # On Windows, use python.exe, not pythonw.exe
1171 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001172 cmdline = [interp, '-u'] + cmdline
1173 if '=' not in query:
1174 cmdline.append(query)
1175 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001176 try:
1177 nbytes = int(length)
1178 except (TypeError, ValueError):
1179 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001180 p = subprocess.Popen(cmdline,
1181 stdin=subprocess.PIPE,
1182 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001183 stderr=subprocess.PIPE,
1184 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001185 )
Georg Brandl24420152008-05-26 16:32:26 +00001186 if self.command.lower() == "post" and nbytes > 0:
1187 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001188 else:
1189 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001190 # throw away additional data [see bug #427345]
1191 while select.select([self.rfile._sock], [], [], 0)[0]:
1192 if not self.rfile._sock.recv(1):
1193 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001194 stdout, stderr = p.communicate(data)
1195 self.wfile.write(stdout)
1196 if stderr:
1197 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001198 p.stderr.close()
1199 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001200 status = p.returncode
1201 if status:
1202 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001203 else:
1204 self.log_message("CGI script exited OK")
1205
1206
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001207def test(HandlerClass=BaseHTTPRequestHandler,
1208 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001209 """Test the HTTP request handler class.
1210
1211 This runs an HTTP server on port 8000 (or the first command line
1212 argument).
1213
1214 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001215 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001216
1217 HandlerClass.protocol_version = protocol
1218 httpd = ServerClass(server_address, HandlerClass)
1219
1220 sa = httpd.socket.getsockname()
1221 print("Serving HTTP on", sa[0], "port", sa[1], "...")
Alexandre Vassalottib5292a22009-04-03 07:16:55 +00001222 try:
1223 httpd.serve_forever()
1224 except KeyboardInterrupt:
1225 print("\nKeyboard interrupt received, exiting.")
1226 httpd.server_close()
1227 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001228
1229if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001230 parser = argparse.ArgumentParser()
1231 parser.add_argument('--cgi', action='store_true',
1232 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001233 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1234 help='Specify alternate bind address '
1235 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001236 parser.add_argument('port', action='store',
1237 default=8000, type=int,
1238 nargs='?',
1239 help='Specify alternate port [default: 8000]')
1240 args = parser.parse_args()
1241 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001242 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001243 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001244 handler_class = SimpleHTTPRequestHandler
1245 test(HandlerClass=handler_class, port=args.port, bind=args.bind)