? config.h Index: Doc/lib/libbasehttp.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libbasehttp.tex,v retrieving revision 1.13 diff -c -r1.13 libbasehttp.tex *** Doc/lib/libbasehttp.tex 2001/07/14 02:50:55 1.13 --- Doc/lib/libbasehttp.tex 2002/01/07 19:33:12 *************** *** 123,131 **** \end{memberdesc} \begin{memberdesc}{protocol_version} ! This specifies the HTTP protocol version used in responses. ! Typically, this should not be overridden. Defaults to ! \code{'HTTP/1.0'}. \end{memberdesc} \begin{memberdesc}{MessageClass} --- 123,134 ---- \end{memberdesc} \begin{memberdesc}{protocol_version} ! This specifies the HTTP protocol version used in responses. If set ! to \code{'HTTP/1.1'}, the server will permit HTTP persistent ! connections; however, your server \emph{must} then include an ! accurate \code{Content-Length} header (using \method{send_header()}) ! in all of its responses to clients. For backwards compatibility, ! the setting defaults to \code{'HTTP/1.0'}. \end{memberdesc} \begin{memberdesc}{MessageClass} *************** *** 148,156 **** A \class{BaseHTTPRequestHandler} instance has the following methods: \begin{methoddesc}{handle}{} ! Overrides the superclass' \method{handle()} method to provide the ! specific handler behavior. This method will parse and dispatch ! the request to the appropriate \method{do_*()} method. \end{methoddesc} \begin{methoddesc}{send_error}{code\optional{, message}} --- 151,166 ---- A \class{BaseHTTPRequestHandler} instance has the following methods: \begin{methoddesc}{handle}{} ! Calls \method{handle_one_request()} once (or, if persistent connections ! are enabled, multiple times) to handle incoming HTTP requests. ! You should never need to override it; instead, implement appropriate ! \method{do_*()} methods. ! \end{methoddesc} ! ! \begin{methoddesc}{handle_one_request}{} ! This method will parse and dispatch ! the request to the appropriate \method{do_*()} method. You should ! never need to override it. \end{methoddesc} \begin{methoddesc}{send_error}{code\optional{, message}} Index: Lib/BaseHTTPServer.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/BaseHTTPServer.py,v retrieving revision 1.17 diff -c -r1.17 BaseHTTPServer.py *** Lib/BaseHTTPServer.py 2001/07/25 06:12:16 1.17 --- Lib/BaseHTTPServer.py 2002/01/07 19:33:13 *************** *** 2,8 **** Note: the class in this module doesn't implement any HTTP request; see SimpleHTTPServer for simple implementations of GET, HEAD and POST ! (including CGI scripts). Contents: --- 2,9 ---- Note: the class in this module doesn't implement any HTTP request; see SimpleHTTPServer for simple implementations of GET, HEAD and POST ! (including CGI scripts). It does, however, optionally implement HTTP/1.1 ! persistent connections, as of version 0.3. Contents: *************** *** 11,22 **** XXX To do: - - send server version - log requests even later (to capture byte count) - log user-agent header and other interesting goodies - send error log to separate file - - are request names really case sensitive? - """ --- 12,20 ---- *************** *** 28,34 **** # Expires September 8, 1995 March 8, 1995 # # URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt ! # Log files # --------- --- 26,40 ---- # Expires September 8, 1995 March 8, 1995 # # URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt ! # ! # and ! # ! # Network Working Group R. Fielding ! # Request for Comments: 2616 et al ! # Obsoletes: 2068 June 1999 ! # Category: Standards Track ! # ! # URL: http://www.faqs.org/rfcs/rfc2616.html # Log files # --------- *************** *** 60,68 **** # (Actually, the latter is only true if you know the server configuration # at the time the request was made!) - __version__ = "0.2" - __all__ = ["HTTPServer", "BaseHTTPRequestHandler"] import sys --- 66,73 ---- # (Actually, the latter is only true if you know the server configuration # at the time the request was made!) + __version__ = "0.3" __all__ = ["HTTPServer", "BaseHTTPRequestHandler"] import sys *************** *** 70,75 **** --- 75,81 ---- import socket # For gethostbyaddr() import mimetools import SocketServer + import cStringIO # Default error message DEFAULT_ERROR_MESSAGE = """\ *************** *** 122,130 **** where is a (case-sensitive) keyword such as GET or POST, is a string containing path information for the request, ! and should be the string "HTTP/1.0". is encoded ! using the URL encoding scheme (using %xx to signify the ASCII ! character with hex code xx). The protocol is vague about whether lines are separated by LF characters or by CRLF pairs -- for compatibility with the widest --- 128,136 ---- where is a (case-sensitive) keyword such as GET or POST, is a string containing path information for the request, ! and should be the string "HTTP/1.0" or "HTTP/1.1". ! is encoded using the URL encoding scheme (using %xx to signify ! the ASCII character with hex code xx). The protocol is vague about whether lines are separated by LF characters or by CRLF pairs -- for compatibility with the widest *************** *** 143,149 **** 0.9 request; this form has no optional headers and data part and the reply consists of just the data. ! The reply form of the HTTP 1.0 protocol again has three parts: 1. One line giving the response code 2. An optional set of RFC-822-style headers --- 149,155 ---- 0.9 request; this form has no optional headers and data part and the reply consists of just the data. ! The reply form of the HTTP 1.x protocol again has three parts: 1. One line giving the response code 2. An optional set of RFC-822-style headers *************** *** 155,161 **** ! where is the protocol version (always "HTTP/1.0"), is a 3-digit response code indicating success or failure of the request, and is an optional human-readable string explaining what the response code means. --- 161,167 ---- ! where is the protocol version ("HTTP/1.0" or "HTTP/1.1"), is a 3-digit response code indicating success or failure of the request, and is an optional human-readable string explaining what the response code means. *************** *** 221,226 **** --- 227,233 ---- """ self.request_version = version = "HTTP/0.9" # Default + self.close_connection = 1 requestline = self.raw_requestline if requestline[-2:] == '\r\n': requestline = requestline[:-2] *************** *** 233,252 **** if version[:5] != 'HTTP/': self.send_error(400, "Bad request version (%s)" % `version`) return 0 elif len(words) == 2: [command, path] = words if command != 'GET': self.send_error(400, "Bad HTTP/0.9 request type (%s)" % `command`) return 0 else: self.send_error(400, "Bad request syntax (%s)" % `requestline`) return 0 self.command, self.path, self.request_version = command, path, version ! self.headers = self.MessageClass(self.rfile, 0) return 1 ! def handle(self): """Handle a single HTTP request. You normally don't need to override this method; see the class --- 240,291 ---- if version[:5] != 'HTTP/': self.send_error(400, "Bad request version (%s)" % `version`) return 0 + try: + version_number = float(version.split('/', 1)[1]) + except ValueError: + self.send_error(400, "Bad request version (%s)" % `version`) + return 0 + if version_number >= 1.1 and self.protocol_version >= "HTTP/1.1": + self.close_connection = 0 + if version_number >= 2.0: + self.send_error(505, + "Invalid HTTP Version (%f)" % version_number) + return 0 elif len(words) == 2: [command, path] = words + self.close_connection = 1 if command != 'GET': self.send_error(400, "Bad HTTP/0.9 request type (%s)" % `command`) return 0 + elif not words: + return 0 else: self.send_error(400, "Bad request syntax (%s)" % `requestline`) return 0 self.command, self.path, self.request_version = command, path, version ! ! # Deal with pipelining ! bytes = "" ! while 1: ! line = self.rfile.readline() ! bytes = bytes + line ! if line == '\r\n' or line == '\n' or line == '': ! break ! ! # Examine the headers and look for a Connection directive ! hfile = cStringIO.StringIO(bytes) ! self.headers = self.MessageClass(hfile) ! ! conntype = self.headers.get('Connection', "") ! if conntype.lower() == 'close': ! self.close_connection = 1 ! elif (conntype.lower() == 'keep-alive' and ! self.protocol_version >= "HTTP/1.1"): ! self.close_connection = 0 return 1 ! def handle_one_request(self): """Handle a single HTTP request. You normally don't need to override this method; see the class *************** *** 254,261 **** commands such as GET and POST. """ - self.raw_requestline = self.rfile.readline() if not self.parse_request(): # An error code has been sent, just exit return mname = 'do_' + self.command --- 293,302 ---- commands such as GET and POST. """ self.raw_requestline = self.rfile.readline() + if not self.raw_requestline: + self.close_connection = 1 + return if not self.parse_request(): # An error code has been sent, just exit return mname = 'do_' + self.command *************** *** 265,270 **** --- 306,319 ---- method = getattr(self, mname) method() + def handle(self): + """Handle multiple requests if necessary.""" + self.close_connection = 1 + + self.handle_one_request() + while not self.close_connection: + self.handle_one_request() + def send_error(self, code, message=None): """Send and log an error reply. *************** *** 278,297 **** """ ! try: ! short, long = self.responses[code] ! except KeyError: ! short, long = '???', '???' if not message: message = short explain = long self.log_error("code %d, message %s", code, message) self.send_response(code, message) self.end_headers() ! self.wfile.write(self.error_message_format % ! {'code': code, ! 'message': message, ! 'explain': explain}) error_message_format = DEFAULT_ERROR_MESSAGE --- 327,347 ---- """ ! short, long = self.responses.get(code, ('???', '???')) if not message: message = short explain = long self.log_error("code %d, message %s", code, message) + content = (self.error_message_format % + {'code': code, 'message': message, 'explain': explain}) self.send_response(code, message) + # if code != 304: + # self.send_header('Content-Length', len(content)) + self.send_header('Content-Type', 'text/html') + self.send_header('Connection', 'close') self.end_headers() ! if self.command != 'HEAD' and code >= 200 and code not in (204, 304): ! self.wfile.write(content) error_message_format = DEFAULT_ERROR_MESSAGE *************** *** 304,316 **** """ self.log_request(code) if message is None: ! if self.responses.has_key(code): message = self.responses[code][0] else: message = '' if self.request_version != 'HTTP/0.9': ! self.wfile.write("%s %s %s\r\n" % ! (self.protocol_version, str(code), message)) self.send_header('Server', self.version_string()) self.send_header('Date', self.date_time_string()) --- 354,367 ---- """ self.log_request(code) if message is None: ! if code in self.responses: message = self.responses[code][0] else: message = '' if self.request_version != 'HTTP/0.9': ! self.wfile.write("%s %d %s\r\n" % ! (self.protocol_version, code, message)) ! # print (self.protocol_version, code, message) self.send_header('Server', self.version_string()) self.send_header('Date', self.date_time_string()) *************** *** 319,324 **** --- 370,381 ---- if self.request_version != 'HTTP/0.9': self.wfile.write("%s: %s\r\n" % (keyword, value)) + if keyword.lower() == 'connection': + if value.lower() == 'close': + self.close_connection = 1 + elif value.lower() == 'keep-alive': + self.close_connection = 0 + def end_headers(self): """Send the blank line ending the MIME headers.""" if self.request_version != 'HTTP/0.9': *************** *** 412,419 **** # Essentially static class variables # The version of the HTTP protocol we support. ! # Don't override unless you know what you're doing (hint: incoming ! # requests are required to have exactly this version string). protocol_version = "HTTP/1.0" # The Message-like class used to parse headers --- 469,475 ---- # Essentially static class variables # The version of the HTTP protocol we support. ! # Set this to HTTP/1.1 to enable automatic keepalive protocol_version = "HTTP/1.0" # The Message-like class used to parse headers *************** *** 423,440 **** # form {code: (shortmessage, longmessage)}. # See http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html responses = { 200: ('OK', 'Request fulfilled, document follows'), 201: ('Created', 'Document created, URL follows'), 202: ('Accepted', 'Request accepted, processing continues off-line'), ! 203: ('Partial information', 'Request fulfilled from cache'), 204: ('No response', 'Request fulfilled, nothing follows'), ! 301: ('Moved', 'Object moved permanently -- see URI list'), 302: ('Found', 'Object moved temporarily -- see URI list'), ! 303: ('Method', 'Object moved -- see Method and URL list'), 304: ('Not modified', ! 'Document has not changed singe given time'), 400: ('Bad request', 'Bad request syntax or unsupported method'), --- 479,509 ---- # form {code: (shortmessage, longmessage)}. # See http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html responses = { + 100: ('Continue', 'Request received, please continue'), + 101: ('Switching Protocols', + 'Switching to new protocol; obey Upgrade header'), + 200: ('OK', 'Request fulfilled, document follows'), 201: ('Created', 'Document created, URL follows'), 202: ('Accepted', 'Request accepted, processing continues off-line'), ! 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), 204: ('No response', 'Request fulfilled, nothing follows'), + 205: ('Reset Content', 'Clear input form for further input.'), + 206: ('Partial Content', 'Partial content follows.'), ! 300: ('Multiple Choices', ! 'Object has several resources -- see URI list'), ! 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), 302: ('Found', 'Object moved temporarily -- see URI list'), ! 303: ('See Other', 'Object moved -- see Method and URL list'), 304: ('Not modified', ! 'Document has not changed since given time'), ! 305: ('Use Proxy', ! 'You must use proxy specified in Location to access this ' ! 'resource.'), ! 307: ('Temporary Redirect', ! 'Object moved temporarily -- see URI list'), 400: ('Bad request', 'Bad request syntax or unsupported method'), *************** *** 444,464 **** 'No payment -- see charging schemes'), 403: ('Forbidden', 'Request forbidden -- authorization will not help'), ! 404: ('Not found', 'Nothing matches the given URI'), 500: ('Internal error', 'Server got itself in trouble'), ! 501: ('Not implemented', 'Server does not support this operation'), ! 502: ('Service temporarily overloaded', 'The server cannot process the request due to a high load'), ! 503: ('Gateway timeout', 'The gateway server did not receive a timely response'), ! } def test(HandlerClass = BaseHTTPRequestHandler, ! ServerClass = HTTPServer): """Test the HTTP request handler class. This runs an HTTP server on port 8000 (or the first command line --- 513,552 ---- 'No payment -- see charging schemes'), 403: ('Forbidden', 'Request forbidden -- authorization will not help'), ! 404: ('Not Found', 'Nothing matches the given URI'), ! 405: ('Method Not Allowed', ! 'Specified method is invalid for this server.'), ! 406: ('Not Acceptable', 'URI not available in preferred format.'), ! 407: ('Proxy Authentication Required', 'You must authenticate with ' ! 'this proxy before proceeding.'), ! 408: ('Request Time-out', 'Request timed out; try again later.'), ! 409: ('Conflict', 'Request conflict.'), ! 410: ('Gone', ! 'URI no longer exists and has been permanently removed.'), ! 411: ('Length Required', 'Client must specify Content-Length.'), ! 412: ('Precondition Failed', 'Precondition in headers is false.'), ! 413: ('Request Entity Too Large', 'Entity is too large.'), ! 414: ('Request-URI Too Long', 'URI is too long.'), ! 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), ! 416: ('Requested Range Not Satisfiable', ! 'Cannot satisfy request range.'), ! 417: ('Expectation Failed', ! 'Expect condition could not be satisfied.'), 500: ('Internal error', 'Server got itself in trouble'), ! 501: ('Not Implemented', 'Server does not support this operation'), ! 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), ! 503: ('Service temporarily overloaded', 'The server cannot process the request due to a high load'), ! 504: ('Gateway timeout', 'The gateway server did not receive a timely response'), ! 505: ('HTTP Version not supported', 'Cannot fulfill request.'), } def test(HandlerClass = BaseHTTPRequestHandler, ! ServerClass = HTTPServer, protocol="HTTP/1.0"): """Test the HTTP request handler class. This runs an HTTP server on port 8000 (or the first command line *************** *** 472,477 **** --- 560,566 ---- port = 8000 server_address = ('', port) + HandlerClass.protocol_version = protocol httpd = ServerClass(server_address, HandlerClass) sa = httpd.socket.getsockname() Index: Lib/SimpleHTTPServer.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/SimpleHTTPServer.py,v retrieving revision 1.17 diff -c -r1.17 SimpleHTTPServer.py *** Lib/SimpleHTTPServer.py 2001/02/09 10:26:06 1.17 --- Lib/SimpleHTTPServer.py 2002/01/07 19:33:13 *************** *** 82,87 **** --- 82,88 ---- return None self.send_response(200) self.send_header("Content-type", ctype) + self.send_header("Content-Length", str(os.fstat(f.fileno())[6])) self.end_headers() return f *************** *** 115,123 **** --- 116,126 ---- # Note: a link to a directory displays with @ and links with / f.write('
  • %s\n' % (linkname, displayname)) f.write("\n
    \n") + length = f.tell() f.seek(0) self.send_response(200) self.send_header("Content-type", "text/html") + self.send_header("Content-Length", str(length)) self.end_headers() return f