blob: ac53550a9722e6599c4c7adc51645ad780c358eb [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
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200103from http import HTTPStatus
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/':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200283 self.send_error(
284 HTTPStatus.BAD_REQUEST,
285 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000286 return False
287 try:
288 base_version_number = version.split('/', 1)[1]
289 version_number = base_version_number.split(".")
290 # RFC 2145 section 3.1 says there can be only one "." and
291 # - major and minor numbers MUST be treated as
292 # separate integers;
293 # - HTTP/2.4 is a lower version than HTTP/2.13, which in
294 # turn is lower than HTTP/12.3;
295 # - Leading zeros MUST be ignored by recipients.
296 if len(version_number) != 2:
297 raise ValueError
298 version_number = int(version_number[0]), int(version_number[1])
299 except (ValueError, IndexError):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200300 self.send_error(
301 HTTPStatus.BAD_REQUEST,
302 "Bad request version (%r)" % version)
Georg Brandl24420152008-05-26 16:32:26 +0000303 return False
304 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
305 self.close_connection = 0
306 if version_number >= (2, 0):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200307 self.send_error(
308 HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
309 "Invalid HTTP Version (%s)" % base_version_number)
Georg Brandl24420152008-05-26 16:32:26 +0000310 return False
311 elif len(words) == 2:
Senthil Kumaran30755492011-12-23 17:03:41 +0800312 command, path = words
Georg Brandl24420152008-05-26 16:32:26 +0000313 self.close_connection = 1
314 if command != 'GET':
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200315 self.send_error(
316 HTTPStatus.BAD_REQUEST,
317 "Bad HTTP/0.9 request type (%r)" % command)
Georg Brandl24420152008-05-26 16:32:26 +0000318 return False
319 elif not words:
320 return False
321 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200322 self.send_error(
323 HTTPStatus.BAD_REQUEST,
324 "Bad request syntax (%r)" % requestline)
Georg Brandl24420152008-05-26 16:32:26 +0000325 return False
326 self.command, self.path, self.request_version = command, path, version
327
328 # Examine the headers and look for a Connection directive.
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000329 try:
330 self.headers = http.client.parse_headers(self.rfile,
331 _class=self.MessageClass)
332 except http.client.LineTooLong:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200333 self.send_error(
334 HTTPStatus.BAD_REQUEST,
335 "Line too long")
Senthil Kumaran5466bf12010-12-18 16:55:23 +0000336 return False
Georg Brandl24420152008-05-26 16:32:26 +0000337
338 conntype = self.headers.get('Connection', "")
339 if conntype.lower() == 'close':
340 self.close_connection = 1
341 elif (conntype.lower() == 'keep-alive' and
342 self.protocol_version >= "HTTP/1.1"):
343 self.close_connection = 0
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000344 # Examine the headers and look for an Expect directive
345 expect = self.headers.get('Expect', "")
346 if (expect.lower() == "100-continue" and
347 self.protocol_version >= "HTTP/1.1" and
348 self.request_version >= "HTTP/1.1"):
349 if not self.handle_expect_100():
350 return False
351 return True
352
353 def handle_expect_100(self):
354 """Decide what to do with an "Expect: 100-continue" header.
355
356 If the client is expecting a 100 Continue response, we must
357 respond with either a 100 Continue or a final response before
358 waiting for the request body. The default is to always respond
359 with a 100 Continue. You can behave differently (for example,
360 reject unauthorized requests) by overriding this method.
361
362 This method should either return True (possibly after sending
363 a 100 Continue response) or send an error response and return
364 False.
365
366 """
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200367 self.send_response_only(HTTPStatus.CONTINUE)
Benjamin Peterson04424232014-01-18 21:50:18 -0500368 self.end_headers()
Georg Brandl24420152008-05-26 16:32:26 +0000369 return True
370
371 def handle_one_request(self):
372 """Handle a single HTTP request.
373
374 You normally don't need to override this method; see the class
375 __doc__ string for information on how to handle specific HTTP
376 commands such as GET and POST.
377
378 """
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000379 try:
Antoine Pitrouc4924372010-12-16 16:48:36 +0000380 self.raw_requestline = self.rfile.readline(65537)
381 if len(self.raw_requestline) > 65536:
382 self.requestline = ''
383 self.request_version = ''
384 self.command = ''
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200385 self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
Antoine Pitrouc4924372010-12-16 16:48:36 +0000386 return
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000387 if not self.raw_requestline:
388 self.close_connection = 1
389 return
390 if not self.parse_request():
391 # An error code has been sent, just exit
392 return
393 mname = 'do_' + self.command
394 if not hasattr(self, mname):
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200395 self.send_error(
396 HTTPStatus.NOT_IMPLEMENTED,
397 "Unsupported method (%r)" % self.command)
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000398 return
399 method = getattr(self, mname)
400 method()
401 self.wfile.flush() #actually send the response if not already done.
402 except socket.timeout as e:
403 #a read or a write timed out. Discard this connection
404 self.log_error("Request timed out: %r", e)
Georg Brandl24420152008-05-26 16:32:26 +0000405 self.close_connection = 1
406 return
Georg Brandl24420152008-05-26 16:32:26 +0000407
408 def handle(self):
409 """Handle multiple requests if necessary."""
410 self.close_connection = 1
411
412 self.handle_one_request()
413 while not self.close_connection:
414 self.handle_one_request()
415
Senthil Kumaran26886442013-03-15 07:53:21 -0700416 def send_error(self, code, message=None, explain=None):
Georg Brandl24420152008-05-26 16:32:26 +0000417 """Send and log an error reply.
418
Senthil Kumaran26886442013-03-15 07:53:21 -0700419 Arguments are
420 * code: an HTTP error code
421 3 digits
422 * message: a simple optional 1 line reason phrase.
423 *( HTAB / SP / VCHAR / %x80-FF )
424 defaults to short entry matching the response code
425 * explain: a detailed message defaults to the long entry
426 matching the response code.
Georg Brandl24420152008-05-26 16:32:26 +0000427
428 This sends an error response (so it must be called before any
429 output has been generated), logs the error, and finally sends
430 a piece of HTML explaining the error to the user.
431
432 """
433
434 try:
435 shortmsg, longmsg = self.responses[code]
436 except KeyError:
437 shortmsg, longmsg = '???', '???'
438 if message is None:
439 message = shortmsg
Senthil Kumaran26886442013-03-15 07:53:21 -0700440 if explain is None:
441 explain = longmsg
Georg Brandl24420152008-05-26 16:32:26 +0000442 self.log_error("code %d, message %s", code, message)
443 # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
444 content = (self.error_message_format %
Senthil Kumaran26886442013-03-15 07:53:21 -0700445 {'code': code, 'message': _quote_html(message), 'explain': _quote_html(explain)})
Senthil Kumaran52d27202012-10-10 23:16:21 -0700446 body = content.encode('UTF-8', 'replace')
Senthil Kumaran1e7551d2013-03-05 02:25:58 -0800447 self.send_response(code, message)
Georg Brandl24420152008-05-26 16:32:26 +0000448 self.send_header("Content-Type", self.error_content_type)
449 self.send_header('Connection', 'close')
Senthil Kumaran52d27202012-10-10 23:16:21 -0700450 self.send_header('Content-Length', int(len(body)))
Georg Brandl24420152008-05-26 16:32:26 +0000451 self.end_headers()
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200452
453 if (self.command != 'HEAD' and
454 code >= 200 and
455 code not in (
456 HTTPStatus.NO_CONTENT, HTTPStatus.NOT_MODIFIED)):
Senthil Kumaran52d27202012-10-10 23:16:21 -0700457 self.wfile.write(body)
Georg Brandl24420152008-05-26 16:32:26 +0000458
459 def send_response(self, code, message=None):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800460 """Add the response header to the headers buffer and log the
461 response code.
Georg Brandl24420152008-05-26 16:32:26 +0000462
463 Also send two standard headers with the server software
464 version and the current date.
465
466 """
467 self.log_request(code)
Senthil Kumaran0f476d42010-09-30 06:09:18 +0000468 self.send_response_only(code, message)
469 self.send_header('Server', self.version_string())
470 self.send_header('Date', self.date_time_string())
471
472 def send_response_only(self, code, message=None):
473 """Send the response header only."""
Georg Brandl24420152008-05-26 16:32:26 +0000474 if message is None:
475 if code in self.responses:
476 message = self.responses[code][0]
477 else:
478 message = ''
479 if self.request_version != 'HTTP/0.9':
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800480 if not hasattr(self, '_headers_buffer'):
481 self._headers_buffer = []
482 self._headers_buffer.append(("%s %d %s\r\n" %
483 (self.protocol_version, code, message)).encode(
484 'latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000485
486 def send_header(self, keyword, value):
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800487 """Send a MIME header to the headers buffer."""
Georg Brandl24420152008-05-26 16:32:26 +0000488 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000489 if not hasattr(self, '_headers_buffer'):
490 self._headers_buffer = []
491 self._headers_buffer.append(
Marc-André Lemburg8f36af72011-02-25 15:42:01 +0000492 ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
Georg Brandl24420152008-05-26 16:32:26 +0000493
494 if keyword.lower() == 'connection':
495 if value.lower() == 'close':
496 self.close_connection = 1
497 elif value.lower() == 'keep-alive':
498 self.close_connection = 0
499
500 def end_headers(self):
501 """Send the blank line ending the MIME headers."""
502 if self.request_version != 'HTTP/0.9':
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000503 self._headers_buffer.append(b"\r\n")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +0800504 self.flush_headers()
505
506 def flush_headers(self):
507 if hasattr(self, '_headers_buffer'):
Senthil Kumarane4dad4f2010-11-21 14:36:14 +0000508 self.wfile.write(b"".join(self._headers_buffer))
509 self._headers_buffer = []
Georg Brandl24420152008-05-26 16:32:26 +0000510
511 def log_request(self, code='-', size='-'):
512 """Log an accepted request.
513
514 This is called by send_response().
515
516 """
517
518 self.log_message('"%s" %s %s',
519 self.requestline, str(code), str(size))
520
521 def log_error(self, format, *args):
522 """Log an error.
523
524 This is called when a request cannot be fulfilled. By
525 default it passes the message on to log_message().
526
527 Arguments are the same as for log_message().
528
529 XXX This should go to the separate error log.
530
531 """
532
533 self.log_message(format, *args)
534
535 def log_message(self, format, *args):
536 """Log an arbitrary message.
537
538 This is used by all other logging functions. Override
539 it if you have specific logging wishes.
540
541 The first argument, FORMAT, is a format string for the
542 message to be logged. If the format string contains
543 any % escapes requiring parameters, they should be
544 specified as subsequent arguments (it's just like
545 printf!).
546
Senthil Kumarandb727b42012-04-29 13:41:03 +0800547 The client ip and current date/time are prefixed to
Georg Brandl24420152008-05-26 16:32:26 +0000548 every message.
549
550 """
551
552 sys.stderr.write("%s - - [%s] %s\n" %
553 (self.address_string(),
554 self.log_date_time_string(),
555 format%args))
556
557 def version_string(self):
558 """Return the server software version string."""
559 return self.server_version + ' ' + self.sys_version
560
561 def date_time_string(self, timestamp=None):
562 """Return the current date and time formatted for a message header."""
563 if timestamp is None:
564 timestamp = time.time()
565 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
566 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
567 self.weekdayname[wd],
568 day, self.monthname[month], year,
569 hh, mm, ss)
570 return s
571
572 def log_date_time_string(self):
573 """Return the current time formatted for logging."""
574 now = time.time()
575 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
576 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
577 day, self.monthname[month], year, hh, mm, ss)
578 return s
579
580 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
581
582 monthname = [None,
583 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
584 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
585
586 def address_string(self):
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800587 """Return the client address."""
Georg Brandl24420152008-05-26 16:32:26 +0000588
Senthil Kumaran1aacba42012-04-29 12:51:54 +0800589 return self.client_address[0]
Georg Brandl24420152008-05-26 16:32:26 +0000590
591 # Essentially static class variables
592
593 # The version of the HTTP protocol we support.
594 # Set this to HTTP/1.1 to enable automatic keepalive
595 protocol_version = "HTTP/1.0"
596
Barry Warsaw820c1202008-06-12 04:06:45 +0000597 # MessageClass used to parse headers
Barry Warsaw820c1202008-06-12 04:06:45 +0000598 MessageClass = http.client.HTTPMessage
Georg Brandl24420152008-05-26 16:32:26 +0000599
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200600 # hack to maintain backwards compatibility
Georg Brandl24420152008-05-26 16:32:26 +0000601 responses = {
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200602 v: (v.phrase, v.description)
603 for v in HTTPStatus.__members__.values()
604 }
Georg Brandl24420152008-05-26 16:32:26 +0000605
606
607class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
608
609 """Simple HTTP request handler with GET and HEAD commands.
610
611 This serves files from the current directory and any of its
612 subdirectories. The MIME type for files is determined by
613 calling the .guess_type() method.
614
615 The GET and HEAD requests are identical except that the HEAD
616 request omits the actual contents of the file.
617
618 """
619
620 server_version = "SimpleHTTP/" + __version__
621
622 def do_GET(self):
623 """Serve a GET request."""
624 f = self.send_head()
625 if f:
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200626 try:
627 self.copyfile(f, self.wfile)
628 finally:
629 f.close()
Georg Brandl24420152008-05-26 16:32:26 +0000630
631 def do_HEAD(self):
632 """Serve a HEAD request."""
633 f = self.send_head()
634 if f:
635 f.close()
636
637 def send_head(self):
638 """Common code for GET and HEAD commands.
639
640 This sends the response code and MIME headers.
641
642 Return value is either a file object (which has to be copied
643 to the outputfile by the caller unless the command was HEAD,
644 and must be closed by the caller under all circumstances), or
645 None, in which case the caller has nothing further to do.
646
647 """
648 path = self.translate_path(self.path)
649 f = None
650 if os.path.isdir(path):
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600651 parts = urllib.parse.urlsplit(self.path)
652 if not parts.path.endswith('/'):
Georg Brandl24420152008-05-26 16:32:26 +0000653 # redirect browser - doing basically what apache does
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200654 self.send_response(HTTPStatus.MOVED_PERMANENTLY)
Benjamin Peterson94cb7a22014-12-26 10:53:43 -0600655 new_parts = (parts[0], parts[1], parts[2] + '/',
656 parts[3], parts[4])
657 new_url = urllib.parse.urlunsplit(new_parts)
658 self.send_header("Location", new_url)
Georg Brandl24420152008-05-26 16:32:26 +0000659 self.end_headers()
660 return None
661 for index in "index.html", "index.htm":
662 index = os.path.join(path, index)
663 if os.path.exists(index):
664 path = index
665 break
666 else:
667 return self.list_directory(path)
668 ctype = self.guess_type(path)
669 try:
670 f = open(path, 'rb')
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200671 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200672 self.send_error(HTTPStatus.NOT_FOUND, "File not found")
Georg Brandl24420152008-05-26 16:32:26 +0000673 return None
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200674 try:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200675 self.send_response(HTTPStatus.OK)
Serhiy Storchaka91b0bc22014-01-25 19:43:02 +0200676 self.send_header("Content-type", ctype)
677 fs = os.fstat(f.fileno())
678 self.send_header("Content-Length", str(fs[6]))
679 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
680 self.end_headers()
681 return f
682 except:
683 f.close()
684 raise
Georg Brandl24420152008-05-26 16:32:26 +0000685
686 def list_directory(self, path):
687 """Helper to produce a directory listing (absent index.html).
688
689 Return value is either a file object, or None (indicating an
690 error). In either case, the headers are sent, making the
691 interface the same as for send_head().
692
693 """
694 try:
695 list = os.listdir(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200696 except OSError:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200697 self.send_error(
698 HTTPStatus.NOT_FOUND,
699 "No permission to list directory")
Georg Brandl24420152008-05-26 16:32:26 +0000700 return None
701 list.sort(key=lambda a: a.lower())
702 r = []
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300703 try:
704 displaypath = urllib.parse.unquote(self.path,
705 errors='surrogatepass')
706 except UnicodeDecodeError:
707 displaypath = urllib.parse.unquote(path)
708 displaypath = html.escape(displaypath)
Ezio Melottica897e92011-11-02 19:33:29 +0200709 enc = sys.getfilesystemencoding()
710 title = 'Directory listing for %s' % displaypath
711 r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
712 '"http://www.w3.org/TR/html4/strict.dtd">')
713 r.append('<html>\n<head>')
714 r.append('<meta http-equiv="Content-Type" '
715 'content="text/html; charset=%s">' % enc)
716 r.append('<title>%s</title>\n</head>' % title)
717 r.append('<body>\n<h1>%s</h1>' % title)
718 r.append('<hr>\n<ul>')
Georg Brandl24420152008-05-26 16:32:26 +0000719 for name in list:
720 fullname = os.path.join(path, name)
721 displayname = linkname = name
722 # Append / for directories or @ for symbolic links
723 if os.path.isdir(fullname):
724 displayname = name + "/"
725 linkname = name + "/"
726 if os.path.islink(fullname):
727 displayname = name + "@"
728 # Note: a link to a directory displays with @ and links with /
Ezio Melottica897e92011-11-02 19:33:29 +0200729 r.append('<li><a href="%s">%s</a></li>'
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300730 % (urllib.parse.quote(linkname,
731 errors='surrogatepass'),
732 html.escape(displayname)))
Ezio Melottica897e92011-11-02 19:33:29 +0200733 r.append('</ul>\n<hr>\n</body>\n</html>\n')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300734 encoded = '\n'.join(r).encode(enc, 'surrogateescape')
Georg Brandl24420152008-05-26 16:32:26 +0000735 f = io.BytesIO()
736 f.write(encoded)
737 f.seek(0)
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200738 self.send_response(HTTPStatus.OK)
Georg Brandl24420152008-05-26 16:32:26 +0000739 self.send_header("Content-type", "text/html; charset=%s" % enc)
740 self.send_header("Content-Length", str(len(encoded)))
741 self.end_headers()
742 return f
743
744 def translate_path(self, path):
745 """Translate a /-separated PATH to the local filename syntax.
746
747 Components that mean special things to the local file system
748 (e.g. drive or directory names) are ignored. (XXX They should
749 probably be diagnosed.)
750
751 """
752 # abandon query parameters
753 path = path.split('?',1)[0]
754 path = path.split('#',1)[0]
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700755 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumaran600b7352013-09-29 18:59:04 -0700756 trailing_slash = path.rstrip().endswith('/')
Serhiy Storchakacb5bc402014-08-17 08:22:11 +0300757 try:
758 path = urllib.parse.unquote(path, errors='surrogatepass')
759 except UnicodeDecodeError:
760 path = urllib.parse.unquote(path)
761 path = posixpath.normpath(path)
Georg Brandl24420152008-05-26 16:32:26 +0000762 words = path.split('/')
763 words = filter(None, words)
764 path = os.getcwd()
765 for word in words:
766 drive, word = os.path.splitdrive(word)
767 head, word = os.path.split(word)
768 if word in (os.curdir, os.pardir): continue
769 path = os.path.join(path, word)
Senthil Kumaran72c238e2013-09-13 00:21:18 -0700770 if trailing_slash:
771 path += '/'
Georg Brandl24420152008-05-26 16:32:26 +0000772 return path
773
774 def copyfile(self, source, outputfile):
775 """Copy all data between two file objects.
776
777 The SOURCE argument is a file object open for reading
778 (or anything with a read() method) and the DESTINATION
779 argument is a file object open for writing (or
780 anything with a write() method).
781
782 The only reason for overriding this would be to change
783 the block size or perhaps to replace newlines by CRLF
784 -- note however that this the default server uses this
785 to copy binary data as well.
786
787 """
788 shutil.copyfileobj(source, outputfile)
789
790 def guess_type(self, path):
791 """Guess the type of a file.
792
793 Argument is a PATH (a filename).
794
795 Return value is a string of the form type/subtype,
796 usable for a MIME Content-type header.
797
798 The default implementation looks the file's extension
799 up in the table self.extensions_map, using application/octet-stream
800 as a default; however it would be permissible (if
801 slow) to look inside the data to make a better guess.
802
803 """
804
805 base, ext = posixpath.splitext(path)
806 if ext in self.extensions_map:
807 return self.extensions_map[ext]
808 ext = ext.lower()
809 if ext in self.extensions_map:
810 return self.extensions_map[ext]
811 else:
812 return self.extensions_map['']
813
814 if not mimetypes.inited:
815 mimetypes.init() # try to read system mime.types
816 extensions_map = mimetypes.types_map.copy()
817 extensions_map.update({
818 '': 'application/octet-stream', # Default
819 '.py': 'text/plain',
820 '.c': 'text/plain',
821 '.h': 'text/plain',
822 })
823
824
825# Utilities for CGIHTTPRequestHandler
826
Senthil Kumarand70846b2012-04-12 02:34:32 +0800827def _url_collapse_path(path):
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000828 """
829 Given a URL path, remove extra '/'s and '.' path elements and collapse
Senthil Kumarand70846b2012-04-12 02:34:32 +0800830 any '..' references and returns a colllapsed path.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000831
832 Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800833 The utility of this function is limited to is_cgi method and helps
834 preventing some security attacks.
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000835
836 Returns: A tuple of (head, tail) where tail is everything after the final /
837 and head is everything before it. Head will always start with a '/' and,
838 if it contains anything else, never have a trailing '/'.
839
840 Raises: IndexError if too many '..' occur within the path.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800841
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000842 """
843 # Similar to os.path.split(os.path.normpath(path)) but specific to URL
844 # path semantics rather than local operating system semantics.
Senthil Kumarand70846b2012-04-12 02:34:32 +0800845 path_parts = path.split('/')
846 head_parts = []
847 for part in path_parts[:-1]:
848 if part == '..':
849 head_parts.pop() # IndexError if more '..' than prior parts
850 elif part and part != '.':
851 head_parts.append( part )
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000852 if path_parts:
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800853 tail_part = path_parts.pop()
Senthil Kumarand70846b2012-04-12 02:34:32 +0800854 if tail_part:
855 if tail_part == '..':
856 head_parts.pop()
857 tail_part = ''
858 elif tail_part == '.':
859 tail_part = ''
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000860 else:
861 tail_part = ''
Senthil Kumarand70846b2012-04-12 02:34:32 +0800862
863 splitpath = ('/' + '/'.join(head_parts), tail_part)
864 collapsed_path = "/".join(splitpath)
865
866 return collapsed_path
867
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000868
869
Georg Brandl24420152008-05-26 16:32:26 +0000870nobody = None
871
872def nobody_uid():
873 """Internal routine to get nobody's uid"""
874 global nobody
875 if nobody:
876 return nobody
877 try:
878 import pwd
Brett Cannoncd171c82013-07-04 17:43:24 -0400879 except ImportError:
Georg Brandl24420152008-05-26 16:32:26 +0000880 return -1
881 try:
882 nobody = pwd.getpwnam('nobody')[2]
883 except KeyError:
Georg Brandlcbd2ab12010-12-04 10:39:14 +0000884 nobody = 1 + max(x[2] for x in pwd.getpwall())
Georg Brandl24420152008-05-26 16:32:26 +0000885 return nobody
886
887
888def executable(path):
889 """Test for executable file."""
Victor Stinnerfb25ba92011-06-20 17:45:54 +0200890 return os.access(path, os.X_OK)
Georg Brandl24420152008-05-26 16:32:26 +0000891
892
893class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
894
895 """Complete HTTP server with GET, HEAD and POST commands.
896
897 GET and HEAD also support running CGI scripts.
898
899 The POST command is *only* implemented for CGI scripts.
900
901 """
902
903 # Determine platform specifics
904 have_fork = hasattr(os, 'fork')
Georg Brandl24420152008-05-26 16:32:26 +0000905
906 # Make rfile unbuffered -- we need to read one line and then pass
907 # the rest to a subprocess, so we can't use buffered input.
908 rbufsize = 0
909
910 def do_POST(self):
911 """Serve a POST request.
912
913 This is only implemented for CGI scripts.
914
915 """
916
917 if self.is_cgi():
918 self.run_cgi()
919 else:
Serhiy Storchakae4db7692014-12-23 16:28:28 +0200920 self.send_error(
921 HTTPStatus.NOT_IMPLEMENTED,
922 "Can only POST to CGI scripts")
Georg Brandl24420152008-05-26 16:32:26 +0000923
924 def send_head(self):
925 """Version of send_head that support CGI scripts"""
926 if self.is_cgi():
927 return self.run_cgi()
928 else:
929 return SimpleHTTPRequestHandler.send_head(self)
930
931 def is_cgi(self):
932 """Test whether self.path corresponds to a CGI script.
933
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000934 Returns True and updates the cgi_info attribute to the tuple
935 (dir, rest) if self.path requires running a CGI script.
936 Returns False otherwise.
Georg Brandl24420152008-05-26 16:32:26 +0000937
Benjamin Petersona7deeee2009-05-08 20:54:42 +0000938 If any exception is raised, the caller should assume that
939 self.path was rejected as invalid and act accordingly.
940
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000941 The default implementation tests whether the normalized url
942 path begins with one of the strings in self.cgi_directories
943 (and the next character is a '/' or the end of the string).
Georg Brandl24420152008-05-26 16:32:26 +0000944
945 """
Benjamin Peterson73b8b1c2014-06-14 18:36:29 -0700946 collapsed_path = _url_collapse_path(urllib.parse.unquote(self.path))
Senthil Kumarand70846b2012-04-12 02:34:32 +0800947 dir_sep = collapsed_path.find('/', 1)
948 head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
Senthil Kumarandbb369d2012-04-11 03:15:28 +0800949 if head in self.cgi_directories:
950 self.cgi_info = head, tail
Benjamin Petersonad71f0f2009-04-11 20:12:10 +0000951 return True
Georg Brandl24420152008-05-26 16:32:26 +0000952 return False
953
Senthil Kumarand70846b2012-04-12 02:34:32 +0800954
Georg Brandl24420152008-05-26 16:32:26 +0000955 cgi_directories = ['/cgi-bin', '/htbin']
956
957 def is_executable(self, path):
958 """Test whether argument path is an executable file."""
959 return executable(path)
960
961 def is_python(self, path):
962 """Test whether argument path is a Python script."""
963 head, tail = os.path.splitext(path)
964 return tail.lower() in (".py", ".pyw")
965
966 def run_cgi(self):
967 """Execute a CGI script."""
Georg Brandl24420152008-05-26 16:32:26 +0000968 dir, rest = self.cgi_info
Ned Deily915a30f2014-07-12 22:06:26 -0700969 path = dir + '/' + rest
970 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000971 while i >= 0:
Ned Deily915a30f2014-07-12 22:06:26 -0700972 nextdir = path[:i]
973 nextrest = path[i+1:]
Georg Brandl24420152008-05-26 16:32:26 +0000974
975 scriptdir = self.translate_path(nextdir)
976 if os.path.isdir(scriptdir):
977 dir, rest = nextdir, nextrest
Ned Deily915a30f2014-07-12 22:06:26 -0700978 i = path.find('/', len(dir)+1)
Georg Brandl24420152008-05-26 16:32:26 +0000979 else:
980 break
981
982 # find an explicit query string, if present.
983 i = rest.rfind('?')
984 if i >= 0:
985 rest, query = rest[:i], rest[i+1:]
986 else:
987 query = ''
988
989 # dissect the part after the directory name into a script name &
990 # a possible additional path, to be stored in PATH_INFO.
991 i = rest.find('/')
992 if i >= 0:
993 script, rest = rest[:i], rest[i:]
994 else:
995 script, rest = rest, ''
996
997 scriptname = dir + '/' + script
998 scriptfile = self.translate_path(scriptname)
999 if not os.path.exists(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001000 self.send_error(
1001 HTTPStatus.NOT_FOUND,
1002 "No such CGI script (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001003 return
1004 if not os.path.isfile(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001005 self.send_error(
1006 HTTPStatus.FORBIDDEN,
1007 "CGI script is not a plain file (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001008 return
1009 ispy = self.is_python(scriptname)
Victor Stinnerfb25ba92011-06-20 17:45:54 +02001010 if self.have_fork or not ispy:
Georg Brandl24420152008-05-26 16:32:26 +00001011 if not self.is_executable(scriptfile):
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001012 self.send_error(
1013 HTTPStatus.FORBIDDEN,
1014 "CGI script is not executable (%r)" % scriptname)
Georg Brandl24420152008-05-26 16:32:26 +00001015 return
1016
1017 # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
1018 # XXX Much of the following could be prepared ahead of time!
Senthil Kumaran42713722010-10-03 17:55:45 +00001019 env = copy.deepcopy(os.environ)
Georg Brandl24420152008-05-26 16:32:26 +00001020 env['SERVER_SOFTWARE'] = self.version_string()
1021 env['SERVER_NAME'] = self.server.server_name
1022 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
1023 env['SERVER_PROTOCOL'] = self.protocol_version
1024 env['SERVER_PORT'] = str(self.server.server_port)
1025 env['REQUEST_METHOD'] = self.command
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001026 uqrest = urllib.parse.unquote(rest)
Georg Brandl24420152008-05-26 16:32:26 +00001027 env['PATH_INFO'] = uqrest
1028 env['PATH_TRANSLATED'] = self.translate_path(uqrest)
1029 env['SCRIPT_NAME'] = scriptname
1030 if query:
1031 env['QUERY_STRING'] = query
Georg Brandl24420152008-05-26 16:32:26 +00001032 env['REMOTE_ADDR'] = self.client_address[0]
Barry Warsaw820c1202008-06-12 04:06:45 +00001033 authorization = self.headers.get("authorization")
Georg Brandl24420152008-05-26 16:32:26 +00001034 if authorization:
1035 authorization = authorization.split()
1036 if len(authorization) == 2:
1037 import base64, binascii
1038 env['AUTH_TYPE'] = authorization[0]
1039 if authorization[0].lower() == "basic":
1040 try:
1041 authorization = authorization[1].encode('ascii')
Georg Brandl706824f2009-06-04 09:42:55 +00001042 authorization = base64.decodebytes(authorization).\
Georg Brandl24420152008-05-26 16:32:26 +00001043 decode('ascii')
1044 except (binascii.Error, UnicodeError):
1045 pass
1046 else:
1047 authorization = authorization.split(':')
1048 if len(authorization) == 2:
1049 env['REMOTE_USER'] = authorization[0]
1050 # XXX REMOTE_IDENT
Barry Warsaw820c1202008-06-12 04:06:45 +00001051 if self.headers.get('content-type') is None:
1052 env['CONTENT_TYPE'] = self.headers.get_content_type()
Georg Brandl24420152008-05-26 16:32:26 +00001053 else:
Barry Warsaw820c1202008-06-12 04:06:45 +00001054 env['CONTENT_TYPE'] = self.headers['content-type']
1055 length = self.headers.get('content-length')
Georg Brandl24420152008-05-26 16:32:26 +00001056 if length:
1057 env['CONTENT_LENGTH'] = length
Barry Warsaw820c1202008-06-12 04:06:45 +00001058 referer = self.headers.get('referer')
Georg Brandl24420152008-05-26 16:32:26 +00001059 if referer:
1060 env['HTTP_REFERER'] = referer
1061 accept = []
1062 for line in self.headers.getallmatchingheaders('accept'):
1063 if line[:1] in "\t\n\r ":
1064 accept.append(line.strip())
1065 else:
1066 accept = accept + line[7:].split(',')
1067 env['HTTP_ACCEPT'] = ','.join(accept)
Barry Warsaw820c1202008-06-12 04:06:45 +00001068 ua = self.headers.get('user-agent')
Georg Brandl24420152008-05-26 16:32:26 +00001069 if ua:
1070 env['HTTP_USER_AGENT'] = ua
Barry Warsaw820c1202008-06-12 04:06:45 +00001071 co = filter(None, self.headers.get_all('cookie', []))
Georg Brandl62e2ca22010-07-31 21:54:24 +00001072 cookie_str = ', '.join(co)
1073 if cookie_str:
1074 env['HTTP_COOKIE'] = cookie_str
Georg Brandl24420152008-05-26 16:32:26 +00001075 # XXX Other HTTP_* headers
1076 # Since we're setting the env in the parent, provide empty
1077 # values to override previously set values
1078 for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
1079 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
1080 env.setdefault(k, "")
Georg Brandl24420152008-05-26 16:32:26 +00001081
Serhiy Storchakae4db7692014-12-23 16:28:28 +02001082 self.send_response(HTTPStatus.OK, "Script output follows")
Senthil Kumaranc7ae19b2011-05-09 23:25:02 +08001083 self.flush_headers()
Georg Brandl24420152008-05-26 16:32:26 +00001084
1085 decoded_query = query.replace('+', ' ')
1086
1087 if self.have_fork:
1088 # Unix -- fork as we should
1089 args = [script]
1090 if '=' not in decoded_query:
1091 args.append(decoded_query)
1092 nobody = nobody_uid()
1093 self.wfile.flush() # Always flush before forking
1094 pid = os.fork()
1095 if pid != 0:
1096 # Parent
1097 pid, sts = os.waitpid(pid, 0)
1098 # throw away additional data [see bug #427345]
1099 while select.select([self.rfile], [], [], 0)[0]:
1100 if not self.rfile.read(1):
1101 break
1102 if sts:
1103 self.log_error("CGI script exit status %#x", sts)
1104 return
1105 # Child
1106 try:
1107 try:
1108 os.setuid(nobody)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +02001109 except OSError:
Georg Brandl24420152008-05-26 16:32:26 +00001110 pass
1111 os.dup2(self.rfile.fileno(), 0)
1112 os.dup2(self.wfile.fileno(), 1)
Senthil Kumaran42713722010-10-03 17:55:45 +00001113 os.execve(scriptfile, args, env)
Georg Brandl24420152008-05-26 16:32:26 +00001114 except:
1115 self.server.handle_error(self.request, self.client_address)
1116 os._exit(127)
1117
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001118 else:
1119 # Non-Unix -- use subprocess
1120 import subprocess
Senthil Kumarane29cd162009-11-11 04:17:53 +00001121 cmdline = [scriptfile]
Georg Brandl24420152008-05-26 16:32:26 +00001122 if self.is_python(scriptfile):
1123 interp = sys.executable
1124 if interp.lower().endswith("w.exe"):
1125 # On Windows, use python.exe, not pythonw.exe
1126 interp = interp[:-5] + interp[-4:]
Senthil Kumarane29cd162009-11-11 04:17:53 +00001127 cmdline = [interp, '-u'] + cmdline
1128 if '=' not in query:
1129 cmdline.append(query)
1130 self.log_message("command: %s", subprocess.list2cmdline(cmdline))
Georg Brandl24420152008-05-26 16:32:26 +00001131 try:
1132 nbytes = int(length)
1133 except (TypeError, ValueError):
1134 nbytes = 0
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001135 p = subprocess.Popen(cmdline,
1136 stdin=subprocess.PIPE,
1137 stdout=subprocess.PIPE,
Senthil Kumaran42713722010-10-03 17:55:45 +00001138 stderr=subprocess.PIPE,
1139 env = env
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001140 )
Georg Brandl24420152008-05-26 16:32:26 +00001141 if self.command.lower() == "post" and nbytes > 0:
1142 data = self.rfile.read(nbytes)
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001143 else:
1144 data = None
Georg Brandl24420152008-05-26 16:32:26 +00001145 # throw away additional data [see bug #427345]
1146 while select.select([self.rfile._sock], [], [], 0)[0]:
1147 if not self.rfile._sock.recv(1):
1148 break
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001149 stdout, stderr = p.communicate(data)
1150 self.wfile.write(stdout)
1151 if stderr:
1152 self.log_error('%s', stderr)
Brian Curtincbad4df2010-11-05 15:04:48 +00001153 p.stderr.close()
1154 p.stdout.close()
Amaury Forgeot d'Arccb0d2d72008-06-18 22:19:22 +00001155 status = p.returncode
1156 if status:
1157 self.log_error("CGI script exit status %#x", status)
Georg Brandl24420152008-05-26 16:32:26 +00001158 else:
1159 self.log_message("CGI script exited OK")
1160
1161
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001162def test(HandlerClass=BaseHTTPRequestHandler,
1163 ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
Georg Brandl24420152008-05-26 16:32:26 +00001164 """Test the HTTP request handler class.
1165
1166 This runs an HTTP server on port 8000 (or the first command line
1167 argument).
1168
1169 """
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001170 server_address = (bind, port)
Georg Brandl24420152008-05-26 16:32:26 +00001171
1172 HandlerClass.protocol_version = protocol
1173 httpd = ServerClass(server_address, HandlerClass)
1174
1175 sa = httpd.socket.getsockname()
1176 print("Serving HTTP on", sa[0], "port", sa[1], "...")
Alexandre Vassalottib5292a22009-04-03 07:16:55 +00001177 try:
1178 httpd.serve_forever()
1179 except KeyboardInterrupt:
1180 print("\nKeyboard interrupt received, exiting.")
1181 httpd.server_close()
1182 sys.exit(0)
Georg Brandl24420152008-05-26 16:32:26 +00001183
1184if __name__ == '__main__':
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001185 parser = argparse.ArgumentParser()
1186 parser.add_argument('--cgi', action='store_true',
1187 help='Run as CGI Server')
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001188 parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
1189 help='Specify alternate bind address '
1190 '[default: all interfaces]')
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001191 parser.add_argument('port', action='store',
1192 default=8000, type=int,
1193 nargs='?',
1194 help='Specify alternate port [default: 8000]')
1195 args = parser.parse_args()
1196 if args.cgi:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001197 handler_class = CGIHTTPRequestHandler
Senthil Kumaran1251faf2012-06-03 16:15:54 +08001198 else:
Senthil Kumarandefe7f42013-09-15 09:37:27 -07001199 handler_class = SimpleHTTPRequestHandler
1200 test(HandlerClass=handler_class, port=args.port, bind=args.bind)