Issue #21793: Added http.HTTPStatus enums (i.e. HTTPStatus.OK,

HTTPStatus.NOT_FOUND).  Patch by Demian Brecht.
This commit is contained in:
Serhiy Storchaka 2014-12-23 16:28:28 +02:00
parent ab47932212
commit e4db76967d
6 changed files with 352 additions and 215 deletions

View file

@ -100,6 +100,8 @@ import urllib.parse
import copy
import argparse
from http import HTTPStatus
# Default error message template
DEFAULT_ERROR_MESSAGE = """\
@ -278,7 +280,9 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
if len(words) == 3:
command, path, version = words
if version[:5] != 'HTTP/':
self.send_error(400, "Bad request version (%r)" % version)
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad request version (%r)" % version)
return False
try:
base_version_number = version.split('/', 1)[1]
@ -293,25 +297,31 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
raise ValueError
version_number = int(version_number[0]), int(version_number[1])
except (ValueError, IndexError):
self.send_error(400, "Bad request version (%r)" % version)
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad request version (%r)" % version)
return False
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 (%s)" % base_version_number)
self.send_error(
HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
"Invalid HTTP Version (%s)" % base_version_number)
return False
elif len(words) == 2:
command, path = words
self.close_connection = 1
if command != 'GET':
self.send_error(400,
"Bad HTTP/0.9 request type (%r)" % command)
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad HTTP/0.9 request type (%r)" % command)
return False
elif not words:
return False
else:
self.send_error(400, "Bad request syntax (%r)" % requestline)
self.send_error(
HTTPStatus.BAD_REQUEST,
"Bad request syntax (%r)" % requestline)
return False
self.command, self.path, self.request_version = command, path, version
@ -320,7 +330,9 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
self.headers = http.client.parse_headers(self.rfile,
_class=self.MessageClass)
except http.client.LineTooLong:
self.send_error(400, "Line too long")
self.send_error(
HTTPStatus.BAD_REQUEST,
"Line too long")
return False
conntype = self.headers.get('Connection', "")
@ -352,7 +364,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
False.
"""
self.send_response_only(100)
self.send_response_only(HTTPStatus.CONTINUE)
self.end_headers()
return True
@ -370,7 +382,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
self.requestline = ''
self.request_version = ''
self.command = ''
self.send_error(414)
self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
return
if not self.raw_requestline:
self.close_connection = 1
@ -380,7 +392,9 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
return
mname = 'do_' + self.command
if not hasattr(self, mname):
self.send_error(501, "Unsupported method (%r)" % self.command)
self.send_error(
HTTPStatus.NOT_IMPLEMENTED,
"Unsupported method (%r)" % self.command)
return
method = getattr(self, mname)
method()
@ -435,7 +449,11 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
self.send_header('Connection', 'close')
self.send_header('Content-Length', int(len(body)))
self.end_headers()
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
if (self.command != 'HEAD' and
code >= 200 and
code not in (
HTTPStatus.NO_CONTENT, HTTPStatus.NOT_MODIFIED)):
self.wfile.write(body)
def send_response(self, code, message=None):
@ -579,82 +597,11 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
# MessageClass used to parse headers
MessageClass = http.client.HTTPMessage
# Table mapping response codes to messages; entries have the
# form {code: (shortmessage, longmessage)}.
# See RFC 2616 and 6585.
# hack to maintain backwards compatibility
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 Content', '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'),
401: ('Unauthorized',
'No permission -- see authorization schemes'),
402: ('Payment Required',
'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 resource.'),
406: ('Not Acceptable', 'URI not available in preferred format.'),
407: ('Proxy Authentication Required', 'You must authenticate with '
'this proxy before proceeding.'),
408: ('Request Timeout', '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.'),
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',
'Server does not support this operation'),
502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
503: ('Service Unavailable',
'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.'),
511: ('Network Authentication Required',
'The client needs to authenticate to gain network access.'),
}
v: (v.phrase, v.description)
for v in HTTPStatus.__members__.values()
}
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
@ -703,7 +650,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
if os.path.isdir(path):
if not self.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(301)
self.send_response(HTTPStatus.MOVED_PERMANENTLY)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
@ -718,10 +665,10 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
try:
f = open(path, 'rb')
except OSError:
self.send_error(404, "File not found")
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
return None
try:
self.send_response(200)
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
self.send_header("Content-Length", str(fs[6]))
@ -743,7 +690,9 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
try:
list = os.listdir(path)
except OSError:
self.send_error(404, "No permission to list directory")
self.send_error(
HTTPStatus.NOT_FOUND,
"No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
r = []
@ -782,7 +731,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
f = io.BytesIO()
f.write(encoded)
f.seek(0)
self.send_response(200)
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "text/html; charset=%s" % enc)
self.send_header("Content-Length", str(len(encoded)))
self.end_headers()
@ -964,7 +913,9 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
if self.is_cgi():
self.run_cgi()
else:
self.send_error(501, "Can only POST to CGI scripts")
self.send_error(
HTTPStatus.NOT_IMPLEMENTED,
"Can only POST to CGI scripts")
def send_head(self):
"""Version of send_head that support CGI scripts"""
@ -1042,17 +993,21 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
scriptname = dir + '/' + script
scriptfile = self.translate_path(scriptname)
if not os.path.exists(scriptfile):
self.send_error(404, "No such CGI script (%r)" % scriptname)
self.send_error(
HTTPStatus.NOT_FOUND,
"No such CGI script (%r)" % scriptname)
return
if not os.path.isfile(scriptfile):
self.send_error(403, "CGI script is not a plain file (%r)" %
scriptname)
self.send_error(
HTTPStatus.FORBIDDEN,
"CGI script is not a plain file (%r)" % scriptname)
return
ispy = self.is_python(scriptname)
if self.have_fork or not ispy:
if not self.is_executable(scriptfile):
self.send_error(403, "CGI script is not executable (%r)" %
scriptname)
self.send_error(
HTTPStatus.FORBIDDEN,
"CGI script is not executable (%r)" % scriptname)
return
# Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
@ -1120,7 +1075,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
env.setdefault(k, "")
self.send_response(200, "Script output follows")
self.send_response(HTTPStatus.OK, "Script output follows")
self.flush_headers()
decoded_query = query.replace('+', ' ')