ยปCore Development>Code coverage>Lib/BaseHTTPServer.py

Python code coverage for Lib/BaseHTTPServer.py

#countcontent
1n/a"""HTTP server base class.
2n/a
3n/aNote: the class in this module doesn't implement any HTTP request; see
4n/aSimpleHTTPServer for simple implementations of GET, HEAD and POST
5n/a(including CGI scripts). It does, however, optionally implement HTTP/1.1
6n/apersistent connections, as of version 0.3.
7n/a
8n/aContents:
9n/a
10n/a- BaseHTTPRequestHandler: HTTP request handler base class
11n/a- test: test function
12n/a
13n/aXXX To do:
14n/a
15n/a- log requests even later (to capture byte count)
16n/a- log user-agent header and other interesting goodies
17n/a- send error log to separate file
181"""
19n/a
20n/a
21n/a# See also:
22n/a#
23n/a# HTTP Working Group T. Berners-Lee
24n/a# INTERNET-DRAFT R. T. Fielding
25n/a# <draft-ietf-http-v10-spec-00.txt> H. Frystyk Nielsen
26n/a# Expires September 8, 1995 March 8, 1995
27n/a#
28n/a# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt
29n/a#
30n/a# and
31n/a#
32n/a# Network Working Group R. Fielding
33n/a# Request for Comments: 2616 et al
34n/a# Obsoletes: 2068 June 1999
35n/a# Category: Standards Track
36n/a#
37n/a# URL: http://www.faqs.org/rfcs/rfc2616.html
38n/a
39n/a# Log files
40n/a# ---------
41n/a#
42n/a# Here's a quote from the NCSA httpd docs about log file format.
43n/a#
44n/a# | The logfile format is as follows. Each line consists of:
45n/a# |
46n/a# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb
47n/a# |
48n/a# | host: Either the DNS name or the IP number of the remote client
49n/a# | rfc931: Any information returned by identd for this person,
50n/a# | - otherwise.
51n/a# | authuser: If user sent a userid for authentication, the user name,
52n/a# | - otherwise.
53n/a# | DD: Day
54n/a# | Mon: Month (calendar name)
55n/a# | YYYY: Year
56n/a# | hh: hour (24-hour format, the machine's timezone)
57n/a# | mm: minutes
58n/a# | ss: seconds
59n/a# | request: The first line of the HTTP request as sent by the client.
60n/a# | ddd: the status code returned by the server, - if not available.
61n/a# | bbbb: the total number of bytes sent,
62n/a# | *not including the HTTP/1.0 header*, - if not available
63n/a# |
64n/a# | You can determine the name of the file accessed through request.
65n/a#
66n/a# (Actually, the latter is only true if you know the server configuration
67n/a# at the time the request was made!)
68n/a
691__version__ = "0.3"
70n/a
711__all__ = ["HTTPServer", "BaseHTTPRequestHandler"]
72n/a
731import sys
741import time
751import socket # For gethostbyaddr()
761from warnings import filterwarnings, catch_warnings
771with catch_warnings():
781 if sys.py3kwarning:
790 filterwarnings("ignore", ".*mimetools has been removed",
800 DeprecationWarning)
811 import mimetools
821import SocketServer
83n/a
84n/a# Default error message template
85n/aDEFAULT_ERROR_MESSAGE = """\
86n/a<head>
87n/a<title>Error response</title>
88n/a</head>
89n/a<body>
90n/a<h1>Error response</h1>
91n/a<p>Error code %(code)d.
92n/a<p>Message: %(message)s.
93n/a<p>Error code explanation: %(code)s = %(explain)s.
94n/a</body>
951"""
96n/a
971DEFAULT_ERROR_CONTENT_TYPE = "text/html"
98n/a
991def _quote_html(html):
10018 return html.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
101n/a
1022class HTTPServer(SocketServer.TCPServer):
103n/a
1041 allow_reuse_address = 1 # Seems to make sense in testing environment
105n/a
1061 def server_bind(self):
107n/a """Override server_bind to store the server name."""
10836 SocketServer.TCPServer.server_bind(self)
10936 host, port = self.socket.getsockname()[:2]
11036 self.server_name = socket.getfqdn(host)
11136 self.server_port = port
112n/a
113n/a
1142class BaseHTTPRequestHandler(SocketServer.StreamRequestHandler):
115n/a
116n/a """HTTP request handler base class.
117n/a
118n/a The following explanation of HTTP serves to guide you through the
119n/a code as well as to expose any misunderstandings I may have about
120n/a HTTP (so you don't need to read the code to figure out I'm wrong
121n/a :-).
122n/a
123n/a HTTP (HyperText Transfer Protocol) is an extensible protocol on
124n/a top of a reliable stream transport (e.g. TCP/IP). The protocol
125n/a recognizes three parts to a request:
126n/a
127n/a 1. One line identifying the request type and path
128n/a 2. An optional set of RFC-822-style headers
129n/a 3. An optional data part
130n/a
131n/a The headers and data are separated by a blank line.
132n/a
133n/a The first line of the request has the form
134n/a
135n/a <command> <path> <version>
136n/a
137n/a where <command> is a (case-sensitive) keyword such as GET or POST,
138n/a <path> is a string containing path information for the request,
139n/a and <version> should be the string "HTTP/1.0" or "HTTP/1.1".
140n/a <path> is encoded using the URL encoding scheme (using %xx to signify
141n/a the ASCII character with hex code xx).
142n/a
143n/a The specification specifies that lines are separated by CRLF but
144n/a for compatibility with the widest range of clients recommends
145n/a servers also handle LF. Similarly, whitespace in the request line
146n/a is treated sensibly (allowing multiple spaces between components
147n/a and allowing trailing whitespace).
148n/a
149n/a Similarly, for output, lines ought to be separated by CRLF pairs
150n/a but most clients grok LF characters just fine.
151n/a
152n/a If the first line of the request has the form
153n/a
154n/a <command> <path>
155n/a
156n/a (i.e. <version> is left out) then this is assumed to be an HTTP
157n/a 0.9 request; this form has no optional headers and data part and
158n/a the reply consists of just the data.
159n/a
160n/a The reply form of the HTTP 1.x protocol again has three parts:
161n/a
162n/a 1. One line giving the response code
163n/a 2. An optional set of RFC-822-style headers
164n/a 3. The data
165n/a
166n/a Again, the headers and data are separated by a blank line.
167n/a
168n/a The response code line has the form
169n/a
170n/a <version> <responsecode> <responsestring>
171n/a
172n/a where <version> is the protocol version ("HTTP/1.0" or "HTTP/1.1"),
173n/a <responsecode> is a 3-digit response code indicating success or
174n/a failure of the request, and <responsestring> is an optional
175n/a human-readable string explaining what the response code means.
176n/a
177n/a This server parses the request and the headers, and then calls a
178n/a function specific to the request type (<command>). Specifically,
179n/a a request SPAM will be handled by a method do_SPAM(). If no
180n/a such method exists the server sends an error response to the
181n/a client. If it exists, it is called with no arguments:
182n/a
183n/a do_SPAM()
184n/a
185n/a Note that the request name is case sensitive (i.e. SPAM and spam
186n/a are different requests).
187n/a
188n/a The various request details are stored in instance variables:
189n/a
190n/a - client_address is the client IP address in the form (host,
191n/a port);
192n/a
193n/a - command, path and version are the broken-down request line;
194n/a
195n/a - headers is an instance of mimetools.Message (or a derived
196n/a class) containing the header information;
197n/a
198n/a - rfile is a file object open for reading positioned at the
199n/a start of the optional input data part;
200n/a
201n/a - wfile is a file object open for writing.
202n/a
203n/a IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING!
204n/a
205n/a The first thing to be written must be the response line. Then
206n/a follow 0 or more header lines, then a blank line, and then the
207n/a actual data (if any). The meaning of the header lines depends on
208n/a the command executed by the server; in most cases, when data is
209n/a returned, there should be at least one header line of the form
210n/a
211n/a Content-type: <type>/<subtype>
212n/a
213n/a where <type> and <subtype> should be registered MIME types,
214n/a e.g. "text/html" or "text/plain".
215n/a
2161 """
217n/a
218n/a # The Python system version, truncated to its first component.
2191 sys_version = "Python/" + sys.version.split()[0]
220n/a
221n/a # The server software version. You may want to override this.
222n/a # The format is multiple whitespace-separated strings,
223n/a # where each string is of the form name[/version].
2241 server_version = "BaseHTTP/" + __version__
225n/a
226n/a # The default request version. This only affects responses up until
227n/a # the point where the request line is parsed, so it mainly decides what
228n/a # the client gets back when sending a malformed request line.
229n/a # Most web servers default to HTTP 0.9, i.e. don't send a status line.
2301 default_request_version = "HTTP/0.9"
231n/a
2321 def parse_request(self):
233n/a """Parse a request (internal).
234n/a
235n/a The request should be stored in self.raw_requestline; the results
236n/a are in self.command, self.path, self.request_version and
237n/a self.headers.
238n/a
239n/a Return True for success, False for failure; on failure, an
240n/a error is sent back.
241n/a
242n/a """
24393 self.command = None # set in case of error on the first line
24493 self.request_version = version = self.default_request_version
24593 self.close_connection = 1
24693 requestline = self.raw_requestline
24793 if requestline[-2:] == '\r\n':
24889 requestline = requestline[:-2]
2494 elif requestline[-1:] == '\n':
2504 requestline = requestline[:-1]
25193 self.requestline = requestline
25293 words = requestline.split()
25393 if len(words) == 3:
25490 [command, path, version] = words
25590 if version[:5] != 'HTTP/':
2561 self.send_error(400, "Bad request version (%r)" % version)
2571 return False
25889 try:
25989 base_version_number = version.split('/', 1)[1]
26089 version_number = base_version_number.split(".")
261n/a # RFC 2145 section 3.1 says there can be only one "." and
262n/a # - major and minor numbers MUST be treated as
263n/a # separate integers;
264n/a # - HTTP/2.4 is a lower version than HTTP/2.13, which in
265n/a # turn is lower than HTTP/12.3;
266n/a # - Leading zeros MUST be ignored by recipients.
26789 if len(version_number) != 2:
2681 raise ValueError
26988 version_number = int(version_number[0]), int(version_number[1])
2701 except (ValueError, IndexError):
2711 self.send_error(400, "Bad request version (%r)" % version)
2721 return False
27388 if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
27426 self.close_connection = 0
27588 if version_number >= (2, 0):
2761 self.send_error(505,
2771 "Invalid HTTP Version (%s)" % base_version_number)
2781 return False
2793 elif len(words) == 2:
2802 [command, path] = words
2812 self.close_connection = 1
2822 if command != 'GET':
2831 self.send_error(400,
2841 "Bad HTTP/0.9 request type (%r)" % command)
2851 return False
2861 elif not words:
2870 return False
288n/a else:
2891 self.send_error(400, "Bad request syntax (%r)" % requestline)
2901 return False
29188 self.command, self.path, self.request_version = command, path, version
292n/a
293n/a # Examine the headers and look for a Connection directive
29488 self.headers = self.MessageClass(self.rfile, 0)
295n/a
29688 conntype = self.headers.get('Connection', "")
29788 if conntype.lower() == 'close':
29821 self.close_connection = 1
29967 elif (conntype.lower() == 'keep-alive' and
3001 self.protocol_version >= "HTTP/1.1"):
3011 self.close_connection = 0
30288 return True
303n/a
3041 def handle_one_request(self):
305n/a """Handle a single HTTP request.
306n/a
307n/a You normally don't need to override this method; see the class
308n/a __doc__ string for information on how to handle specific HTTP
309n/a commands such as GET and POST.
310n/a
311n/a """
31298 try:
31398 self.raw_requestline = self.rfile.readline()
31498 if not self.raw_requestline:
3158 self.close_connection = 1
3168 return
31790 if not self.parse_request():
318n/a # An error code has been sent, just exit
3195 return
32085 mname = 'do_' + self.command
32185 if not hasattr(self, mname):
3228 self.send_error(501, "Unsupported method (%r)" % self.command)
3238 return
32477 method = getattr(self, mname)
32577 method()
32677 self.wfile.flush() #actually send the response if not already done.
3270 except socket.timeout, e:
328n/a #a read or a write timed out. Discard this connection
3290 self.log_error("Request timed out: %r", e)
3300 self.close_connection = 1
3310 return
332n/a
3331 def handle(self):
334n/a """Handle multiple requests if necessary."""
33581 self.close_connection = 1
336n/a
33781 self.handle_one_request()
33898 while not self.close_connection:
33917 self.handle_one_request()
340n/a
3411 def send_error(self, code, message=None):
342n/a """Send and log an error reply.
343n/a
344n/a Arguments are the error code, and a detailed message.
345n/a The detailed message defaults to the short entry matching the
346n/a response code.
347n/a
348n/a This sends an error response (so it must be called before any
349n/a output has been generated), logs the error, and finally sends
350n/a a piece of HTML explaining the error to the user.
351n/a
352n/a """
353n/a
35418 try:
35518 short, long = self.responses[code]
3561 except KeyError:
3571 short, long = '???', '???'
35818 if message is None:
3591 message = short
36018 explain = long
36118 self.log_error("code %d, message %s", code, message)
362n/a # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
36318 content = (self.error_message_format %
36418 {'code': code, 'message': _quote_html(message), 'explain': explain})
36518 self.send_response(code, message)
36618 self.send_header("Content-Type", self.error_content_type)
36718 self.send_header('Connection', 'close')
36818 self.end_headers()
36918 if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
37018 self.wfile.write(content)
371n/a
3721 error_message_format = DEFAULT_ERROR_MESSAGE
3731 error_content_type = DEFAULT_ERROR_CONTENT_TYPE
374n/a
3751 def send_response(self, code, message=None):
376n/a """Send the response header and log the response code.
377n/a
378n/a Also send two standard headers with the server software
379n/a version and the current date.
380n/a
381n/a """
38290 self.log_request(code)
38390 if message is None:
38456 if code in self.responses:
38555 message = self.responses[code][0]
386n/a else:
3871 message = ''
38890 if self.request_version != 'HTTP/0.9':
38990 self.wfile.write("%s %d %s\r\n" %
39090 (self.protocol_version, code, message))
391n/a # print (self.protocol_version, code, message)
39290 self.send_header('Server', self.version_string())
39390 self.send_header('Date', self.date_time_string())
394n/a
3951 def send_header(self, keyword, value):
396n/a """Send a MIME header."""
397345 if self.request_version != 'HTTP/0.9':
398345 self.wfile.write("%s: %s\r\n" % (keyword, value))
399n/a
400345 if keyword.lower() == 'connection':
40122 if value.lower() == 'close':
40221 self.close_connection = 1
4031 elif value.lower() == 'keep-alive':
4041 self.close_connection = 0
405n/a
4061 def end_headers(self):
407n/a """Send the blank line ending the MIME headers."""
40886 if self.request_version != 'HTTP/0.9':
40986 self.wfile.write("\r\n")
410n/a
4111 def log_request(self, code='-', size='-'):
412n/a """Log an accepted request.
413n/a
414n/a This is called by send_response().
415n/a
416n/a """
417n/a
41855 self.log_message('"%s" %s %s',
41955 self.requestline, str(code), str(size))
420n/a
4211 def log_error(self, format, *args):
422n/a """Log an error.
423n/a
424n/a This is called when a request cannot be fulfilled. By
425n/a default it passes the message on to log_message().
426n/a
427n/a Arguments are the same as for log_message().
428n/a
429n/a XXX This should go to the separate error log.
430n/a
431n/a """
432n/a
43318 self.log_message(format, *args)
434n/a
4351 def log_message(self, format, *args):
436n/a """Log an arbitrary message.
437n/a
438n/a This is used by all other logging functions. Override
439n/a it if you have specific logging wishes.
440n/a
441n/a The first argument, FORMAT, is a format string for the
442n/a message to be logged. If the format string contains
443n/a any % escapes requiring parameters, they should be
444n/a specified as subsequent arguments (it's just like
445n/a printf!).
446n/a
447n/a The client host and current date/time are prefixed to
448n/a every message.
449n/a
450n/a """
451n/a
4523 sys.stderr.write("%s - - [%s] %s\n" %
4533 (self.address_string(),
4543 self.log_date_time_string(),
4553 format%args))
456n/a
4571 def version_string(self):
458n/a """Return the server software version string."""
45994 return self.server_version + ' ' + self.sys_version
460n/a
4611 def date_time_string(self, timestamp=None):
462n/a """Return the current date and time formatted for a message header."""
46394 if timestamp is None:
46490 timestamp = time.time()
46594 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
46694 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
46794 self.weekdayname[wd],
46894 day, self.monthname[month], year,
46994 hh, mm, ss)
47094 return s
471n/a
4721 def log_date_time_string(self):
473n/a """Return the current time formatted for logging."""
4743 now = time.time()
4753 year, month, day, hh, mm, ss, x, y, z = time.localtime(now)
4763 s = "%02d/%3s/%04d %02d:%02d:%02d" % (
4773 day, self.monthname[month], year, hh, mm, ss)
4783 return s
479n/a
4801 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
481n/a
4821 monthname = [None,
4831 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
4841 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
485n/a
4861 def address_string(self):
487n/a """Return the client address formatted for logging.
488n/a
489n/a This version looks up the full hostname using gethostbyaddr(),
490n/a and tries to find a name that contains at least one dot.
491n/a
492n/a """
493n/a
49410 host, port = self.client_address[:2]
49510 return socket.getfqdn(host)
496n/a
497n/a # Essentially static class variables
498n/a
499n/a # The version of the HTTP protocol we support.
500n/a # Set this to HTTP/1.1 to enable automatic keepalive
5011 protocol_version = "HTTP/1.0"
502n/a
503n/a # The Message-like class used to parse headers
5041 MessageClass = mimetools.Message
505n/a
506n/a # Table mapping response codes to messages; entries have the
507n/a # form {code: (shortmessage, longmessage)}.
508n/a # See RFC 2616.
5091 responses = {
5101 100: ('Continue', 'Request received, please continue'),
5110 101: ('Switching Protocols',
5121 'Switching to new protocol; obey Upgrade header'),
513n/a
5141 200: ('OK', 'Request fulfilled, document follows'),
5151 201: ('Created', 'Document created, URL follows'),
5160 202: ('Accepted',
5171 'Request accepted, processing continues off-line'),
5181 203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
5191 204: ('No Content', 'Request fulfilled, nothing follows'),
5201 205: ('Reset Content', 'Clear input form for further input.'),
5211 206: ('Partial Content', 'Partial content follows.'),
522n/a
5230 300: ('Multiple Choices',
5241 'Object has several resources -- see URI list'),
5251 301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
5261 302: ('Found', 'Object moved temporarily -- see URI list'),
5271 303: ('See Other', 'Object moved -- see Method and URL list'),
5280 304: ('Not Modified',
5291 'Document has not changed since given time'),
5300 305: ('Use Proxy',
5311 'You must use proxy specified in Location to access this '
532n/a 'resource.'),
5330 307: ('Temporary Redirect',
5341 'Object moved temporarily -- see URI list'),
535n/a
5360 400: ('Bad Request',
5371 'Bad request syntax or unsupported method'),
5380 401: ('Unauthorized',
5391 'No permission -- see authorization schemes'),
5400 402: ('Payment Required',
5411 'No payment -- see charging schemes'),
5420 403: ('Forbidden',
5431 'Request forbidden -- authorization will not help'),
5441 404: ('Not Found', 'Nothing matches the given URI'),
5450 405: ('Method Not Allowed',
5461 'Specified method is invalid for this resource.'),
5471 406: ('Not Acceptable', 'URI not available in preferred format.'),
5481 407: ('Proxy Authentication Required', 'You must authenticate with '
549n/a 'this proxy before proceeding.'),
5501 408: ('Request Timeout', 'Request timed out; try again later.'),
5511 409: ('Conflict', 'Request conflict.'),
5520 410: ('Gone',
5531 'URI no longer exists and has been permanently removed.'),
5541 411: ('Length Required', 'Client must specify Content-Length.'),
5551 412: ('Precondition Failed', 'Precondition in headers is false.'),
5561 413: ('Request Entity Too Large', 'Entity is too large.'),
5571 414: ('Request-URI Too Long', 'URI is too long.'),
5581 415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
5590 416: ('Requested Range Not Satisfiable',
5601 'Cannot satisfy request range.'),
5610 417: ('Expectation Failed',
5621 'Expect condition could not be satisfied.'),
563n/a
5641 500: ('Internal Server Error', 'Server got itself in trouble'),
5650 501: ('Not Implemented',
5661 'Server does not support this operation'),
5671 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
5680 503: ('Service Unavailable',
5691 'The server cannot process the request due to a high load'),
5700 504: ('Gateway Timeout',
5711 'The gateway server did not receive a timely response'),
5721 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
573n/a }
574n/a
575n/a
5761def test(HandlerClass = BaseHTTPRequestHandler,
5771 ServerClass = HTTPServer, protocol="HTTP/1.0"):
578n/a """Test the HTTP request handler class.
579n/a
580n/a This runs an HTTP server on port 8000 (or the first command line
581n/a argument).
582n/a
583n/a """
584n/a
5850 if sys.argv[1:]:
5860 port = int(sys.argv[1])
587n/a else:
5880 port = 8000
5890 server_address = ('', port)
590n/a
5910 HandlerClass.protocol_version = protocol
5920 httpd = ServerClass(server_address, HandlerClass)
593n/a
5940 sa = httpd.socket.getsockname()
5950 print "Serving HTTP on", sa[0], "port", sa[1], "..."
5960 httpd.serve_forever()
597n/a
598n/a
5991if __name__ == '__main__':
6000 test()