diff options
Diffstat (limited to 'Lib/http/server.py')
-rw-r--r-- | Lib/http/server.py | 97 |
1 files changed, 58 insertions, 39 deletions
diff --git a/Lib/http/server.py b/Lib/http/server.py index 5569037..c4ac703 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -100,11 +100,14 @@ import sys import time import urllib.parse import copy +import argparse + # Default error message template DEFAULT_ERROR_MESSAGE = """\ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>Error response</title> @@ -352,6 +355,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): """ self.send_response_only(100) + self.flush_headers() return True def handle_one_request(self): @@ -429,7 +433,8 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): self.wfile.write(content.encode('UTF-8', 'replace')) def send_response(self, code, message=None): - """Send the response header and log the response code. + """Add the response header to the headers buffer and log the + response code. Also send two standard headers with the server software version and the current date. @@ -448,16 +453,19 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): else: message = '' if self.request_version != 'HTTP/0.9': - self.wfile.write(("%s %d %s\r\n" % - (self.protocol_version, code, message)).encode('latin1', 'strict')) + if not hasattr(self, '_headers_buffer'): + self._headers_buffer = [] + self._headers_buffer.append(("%s %d %s\r\n" % + (self.protocol_version, code, message)).encode( + 'latin-1', 'strict')) def send_header(self, keyword, value): - """Send a MIME header.""" + """Send a MIME header to the headers buffer.""" if self.request_version != 'HTTP/0.9': if not hasattr(self, '_headers_buffer'): self._headers_buffer = [] self._headers_buffer.append( - ("%s: %s\r\n" % (keyword, value)).encode('latin1', 'strict')) + ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) if keyword.lower() == 'connection': if value.lower() == 'close': @@ -469,6 +477,10 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): """Send the blank line ending the MIME headers.""" if self.request_version != 'HTTP/0.9': self._headers_buffer.append(b"\r\n") + self.flush_headers() + + def flush_headers(self): + if hasattr(self, '_headers_buffer'): self.wfile.write(b"".join(self._headers_buffer)) self._headers_buffer = [] @@ -514,7 +526,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): """ sys.stderr.write("%s - - [%s] %s\n" % - (self.client_address[0], + (self.address_string(), self.log_date_time_string(), format%args)) @@ -548,15 +560,9 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] def address_string(self): - """Return the client address formatted for logging. - - This version looks up the full hostname using gethostbyaddr(), - and tries to find a name that contains at least one dot. - - """ + """Return the client address.""" - host, port = self.client_address[:2] - return socket.getfqdn(host) + return self.client_address[0] # Essentially static class variables @@ -569,7 +575,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): # Table mapping response codes to messages; entries have the # form {code: (shortmessage, longmessage)}. - # See RFC 2616. + # See RFC 2616 and 6585. responses = { 100: ('Continue', 'Request received, please continue'), 101: ('Switching Protocols', @@ -624,6 +630,12 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): 'Cannot satisfy request range.'), 417: ('Expectation Failed', 'Expect condition could not be satisfied.'), + 428: ('Precondition Required', + 'The origin server requires the request to be conditional.'), + 429: ('Too Many Requests', 'The user has sent too many requests ' + 'in a given amount of time ("rate limiting").'), + 431: ('Request Header Fields Too Large', 'The server is unwilling to ' + 'process the request because its header fields are too large.'), 500: ('Internal Server Error', 'Server got itself in trouble'), 501: ('Not Implemented', @@ -634,6 +646,8 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): 504: ('Gateway Timeout', 'The gateway server did not receive a timely response'), 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), + 511: ('Network Authentication Required', + 'The client needs to authenticate to gain network access.'), } @@ -722,10 +736,16 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): list.sort(key=lambda a: a.lower()) r = [] displaypath = html.escape(urllib.parse.unquote(self.path)) - r.append('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">') - r.append("<html>\n<title>Directory listing for %s</title>\n" % displaypath) - r.append("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath) - r.append("<hr>\n<ul>\n") + enc = sys.getfilesystemencoding() + title = 'Directory listing for %s' % displaypath + r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ' + '"http://www.w3.org/TR/html4/strict.dtd">') + r.append('<html>\n<head>') + r.append('<meta http-equiv="Content-Type" ' + 'content="text/html; charset=%s">' % enc) + r.append('<title>%s</title>\n</head>' % title) + r.append('<body>\n<h1>%s</h1>' % title) + r.append('<hr>\n<ul>') for name in list: fullname = os.path.join(path, name) displayname = linkname = name @@ -736,11 +756,10 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): if os.path.islink(fullname): displayname = name + "@" # Note: a link to a directory displays with @ and links with / - r.append('<li><a href="%s">%s</a>\n' + r.append('<li><a href="%s">%s</a></li>' % (urllib.parse.quote(linkname), html.escape(displayname))) - r.append("</ul>\n<hr>\n</body>\n</html>\n") - enc = sys.getfilesystemencoding() - encoded = ''.join(r).encode(enc) + r.append('</ul>\n<hr>\n</body>\n</html>\n') + encoded = '\n'.join(r).encode(enc) f = io.BytesIO() f.write(encoded) f.seek(0) @@ -888,11 +907,7 @@ def nobody_uid(): def executable(path): """Test for executable file.""" - try: - st = os.stat(path) - except os.error: - return False - return st.st_mode & 0o111 != 0 + return os.access(path, os.X_OK) class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): @@ -1008,7 +1023,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): scriptname) return ispy = self.is_python(scriptname) - if not ispy: + if self.have_fork or not ispy: if not self.is_executable(scriptfile): self.send_error(403, "CGI script is not executable (%r)" % scriptname) @@ -1029,9 +1044,6 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): env['SCRIPT_NAME'] = scriptname if query: env['QUERY_STRING'] = query - host = self.address_string() - if host != self.client_address[0]: - env['REMOTE_HOST'] = host env['REMOTE_ADDR'] = self.client_address[0] authorization = self.headers.get("authorization") if authorization: @@ -1083,6 +1095,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): env.setdefault(k, "") self.send_response(200, "Script output follows") + self.flush_headers() decoded_query = query.replace('+', ' ') @@ -1162,18 +1175,13 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): def test(HandlerClass = BaseHTTPRequestHandler, - ServerClass = HTTPServer, protocol="HTTP/1.0"): + ServerClass = HTTPServer, protocol="HTTP/1.0", port=8000): """Test the HTTP request handler class. This runs an HTTP server on port 8000 (or the first command line argument). """ - - if sys.argv[1:]: - port = int(sys.argv[1]) - else: - port = 8000 server_address = ('', port) HandlerClass.protocol_version = protocol @@ -1189,4 +1197,15 @@ def test(HandlerClass = BaseHTTPRequestHandler, sys.exit(0) if __name__ == '__main__': - test(HandlerClass=SimpleHTTPRequestHandler) + parser = argparse.ArgumentParser() + parser.add_argument('--cgi', action='store_true', + help='Run as CGI Server') + parser.add_argument('port', action='store', + default=8000, type=int, + nargs='?', + help='Specify alternate port [default: 8000]') + args = parser.parse_args() + if args.cgi: + test(HandlerClass=CGIHTTPRequestHandler, port=args.port) + else: + test(HandlerClass=SimpleHTTPRequestHandler, port=args.port) |