mirror of
https://github.com/python/cpython.git
synced 2025-12-15 21:44:50 +00:00
Issue #21793: Added http.HTTPStatus enums (i.e. HTTPStatus.OK,
HTTPStatus.NOT_FOUND). Patch by Demian Brecht.
This commit is contained in:
parent
ab47932212
commit
e4db76967d
6 changed files with 352 additions and 215 deletions
|
|
@ -1,7 +1,16 @@
|
|||
:mod:`http` --- HTTP modules
|
||||
============================
|
||||
|
||||
``http`` is a package that collects several modules for working with the
|
||||
.. module:: http
|
||||
:synopsis: HTTP status codes and messages
|
||||
|
||||
.. index::
|
||||
pair: HTTP; protocol
|
||||
single: HTTP; http (standard module)
|
||||
|
||||
**Source code:** :source:`Lib/http/__init__.py`
|
||||
|
||||
:mod:`http` is a also package that collects several modules for working with the
|
||||
HyperText Transfer Protocol:
|
||||
|
||||
* :mod:`http.client` is a low-level HTTP protocol client; for high-level URL
|
||||
|
|
@ -9,3 +18,113 @@ HyperText Transfer Protocol:
|
|||
* :mod:`http.server` contains basic HTTP server classes based on :mod:`socketserver`
|
||||
* :mod:`http.cookies` has utilities for implementing state management with cookies
|
||||
* :mod:`http.cookiejar` provides persistence of cookies
|
||||
|
||||
:mod:`http` is also a module that defines a number of HTTP status codes and
|
||||
associated messages through the :class:`http.HTTPStatus` enum:
|
||||
|
||||
.. class:: HTTPStatus
|
||||
|
||||
A subclass of :class:`enum.IntEnum` that defines a set of HTTP status codes,
|
||||
reason phrases and long descriptions written in English.
|
||||
|
||||
Usage::
|
||||
|
||||
>>> from http import HTTPStatus
|
||||
>>> HTTPStatus.OK
|
||||
<HTTPStatus.OK: 200>
|
||||
>>> HTTPStatus.OK == 200
|
||||
True
|
||||
>>> http.HTTPStatus.OK.value
|
||||
200
|
||||
>>> HTTPStatus.OK.phrase
|
||||
'OK'
|
||||
>>> HTTPStatus.OK.description
|
||||
'Request fulfilled, document follows'
|
||||
>>> list(HTTPStatus)
|
||||
[<HTTPStatus.CONTINUE: 100>, <HTTPStatus.SWITCHING_PROTOCOLS: 101>, ...]
|
||||
|
||||
.. versionadded:: 3.5
|
||||
Added the *HTTPStatus* Enum
|
||||
|
||||
The supported HTTP status codes are:
|
||||
|
||||
=== ==============================
|
||||
100 Continue
|
||||
101 Switching Protocols
|
||||
102 Processing
|
||||
200 OK
|
||||
201 Created
|
||||
202 Accepted
|
||||
203 Non-Authoritative Information
|
||||
204 No Content
|
||||
205 Reset Content
|
||||
206 Partial Content
|
||||
207 Multi-Status
|
||||
208 Already Reported
|
||||
226 IM Used
|
||||
300 Multiple Choices
|
||||
301 Moved Permanently
|
||||
302 Found
|
||||
303 See Other
|
||||
304 Not Modified
|
||||
305 Use Proxy
|
||||
306 Switch Proxy
|
||||
307 Temporary Redirect
|
||||
308 Permanent Redirect
|
||||
400 Bad Request
|
||||
401 Unauthorized
|
||||
402 Payment Required
|
||||
403 Forbidden
|
||||
404 Not Found
|
||||
405 Method Not Allowed
|
||||
406 Not Acceptable
|
||||
407 Proxy Authentication Required
|
||||
408 Request Timeout
|
||||
409 Conflict
|
||||
410 Gone
|
||||
411 Length Required
|
||||
412 Precondition Failed
|
||||
413 Request Entity Too Large
|
||||
414 Request URI Too Long
|
||||
415 Unsupported Media Type
|
||||
416 Request Range Not Satisfiable
|
||||
417 Expectation Failed
|
||||
418 I'm a teapot
|
||||
419 Authentication Timeout
|
||||
420 Method Failure *(Spring framework)*
|
||||
422 Unprocessable Entity
|
||||
423 Locked
|
||||
424 Failed Dependency
|
||||
426 Upgrade Required
|
||||
428 Precondition Required
|
||||
429 Too Many Requests
|
||||
431 Request Header Field Too Large
|
||||
440 Login Timeout *(Microsoft)*
|
||||
444 No Response *(Nginx)*
|
||||
449 Retry With *(Microsoft)*
|
||||
450 Blocked By Windows Parental Controls *(Microsoft)*
|
||||
494 Request Header Too Large *(Nginx)*
|
||||
495 Cert Error *(Nginx)*
|
||||
496 No Cert *(Nginx)*
|
||||
497 HTTP To HTTPS *(Nginx)*
|
||||
499 Client Closed Request *(Nginx)*
|
||||
500 Internal Server Error
|
||||
501 Not Implemented
|
||||
502 Bad Gateway
|
||||
503 Service Unavailable
|
||||
504 Gateway Timeout
|
||||
505 HTTP Version Not Supported
|
||||
506 Variant Also Negotiates
|
||||
507 Insufficient Storage
|
||||
508 Loop Detected
|
||||
509 Bandwidth Limit Exceeded
|
||||
510 Not Extended
|
||||
511 Network Authentication Required
|
||||
520 Origin Error *(CloudFlare)*
|
||||
521 Web Server Is Down *(CloudFlare)*
|
||||
522 Connection Timed Out *(CloudFlare)*
|
||||
523 Proxy Declined Request *(CloudFlare)*
|
||||
524 A Timeout Occurred *(CloudFlare)*
|
||||
598 Network Read Timeout Error
|
||||
599 Network Connect Timeout Error
|
||||
=== ==============================
|
||||
|
|
|
|||
|
|
@ -451,6 +451,11 @@ Changes in the Python API
|
|||
**without** caching ``None`` in :data:`sys.path_importer_cache` which is
|
||||
different than the typical case (:issue:`22834`).
|
||||
|
||||
* HTTP status code and messages from `http.client` and `http.server` were
|
||||
refactored into a common :class:`~http.HTTPStatus` enum. The values in
|
||||
`http.client` and `http.server` remain available for backwards compatibility.
|
||||
(Contributed by Demian Brecht in :issue:`21793`.)
|
||||
|
||||
Changes in the C API
|
||||
--------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,164 @@
|
|||
# This directory is a Python package.
|
||||
from enum import IntEnum
|
||||
|
||||
__all__ = ['HTTPStatus']
|
||||
|
||||
class HTTPStatus(IntEnum):
|
||||
"""HTTP status codes and reason phrases
|
||||
|
||||
Status codes from the following RFCs are all observed:
|
||||
|
||||
* RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616
|
||||
* RFC 6585: Additional HTTP Status Codes
|
||||
* RFC 3229: Delta encoding in HTTP
|
||||
* RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518
|
||||
* RFC 5842: Binding Extensions to WebDAV
|
||||
* RFC 7238: Permanent Redirect
|
||||
* RFC 2324: Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)
|
||||
* RFC 2295: Transparent Content Negotiation in HTTP
|
||||
* RFC 2774: An HTTP Extension Framework
|
||||
|
||||
Non-standard vendor codes include:
|
||||
|
||||
* Spring framework: 420
|
||||
* Nginx: 444, 494, 495, 496, 497, 499
|
||||
* Microsoft: 440, 449, 450
|
||||
* Cloudflare: 520, 521, 522, 523, 524, 598, 599
|
||||
"""
|
||||
def __new__(cls, value, phrase, description=''):
|
||||
obj = int.__new__(cls, value)
|
||||
obj._value_ = value
|
||||
|
||||
obj.phrase = phrase
|
||||
obj.description = description
|
||||
return obj
|
||||
|
||||
# informational
|
||||
CONTINUE = 100, 'Continue', 'Request received, please continue'
|
||||
SWITCHING_PROTOCOLS = (101, 'Switching Protocols',
|
||||
'Switching to new protocol; obey Upgrade header')
|
||||
PROCESSING = 102, 'Processing'
|
||||
|
||||
# success
|
||||
OK = 200, 'OK', 'Request fulfilled, document follows'
|
||||
CREATED = 201, 'Created', 'Document created, URL follows'
|
||||
ACCEPTED = (202, 'Accepted',
|
||||
'Request accepted, processing continues off-line')
|
||||
NON_AUTHORITATIVE_INFORMATION = (203,
|
||||
'Non-Authoritative Information', 'Request fulfilled from cache')
|
||||
NO_CONTENT = 204, 'No Content', 'Request fulfilled, nothing follows'
|
||||
RESET_CONTENT = 205, 'Reset Content', 'Clear input form for further input'
|
||||
PARTIAL_CONTENT = 206, 'Partial Content', 'Partial content follows'
|
||||
MULTI_STATUS = 207, 'Multi-Status'
|
||||
ALREADY_REPORTED = 208, 'Already Reported'
|
||||
IM_USED = 226, 'IM Used'
|
||||
|
||||
# redirection
|
||||
MULTIPLE_CHOICES = (300, 'Multiple Choices',
|
||||
'Object has several resources -- see URI list')
|
||||
MOVED_PERMANENTLY = (301, 'Moved Permanently',
|
||||
'Object moved permanently -- see URI list')
|
||||
FOUND = 302, 'Found', 'Object moved temporarily -- see URI list'
|
||||
SEE_OTHER = 303, 'See Other', 'Object moved -- see Method and URL list'
|
||||
NOT_MODIFIED = (304, 'Not Modified',
|
||||
'Document has not changed since given time')
|
||||
USE_PROXY = (305, 'Use Proxy',
|
||||
'You must use proxy specified in Location to access this resource')
|
||||
SWITCH_PROXY = 306, 'Switch Proxy'
|
||||
TEMPORARY_REDIRECT = (307, 'Temporary Redirect',
|
||||
'Object moved temporarily -- see URI list')
|
||||
PERMANENT_REDIRECT = (308, 'Permanent Redirect',
|
||||
'Object moved temporarily -- see URI list')
|
||||
|
||||
# client error
|
||||
BAD_REQUEST = (400, 'Bad Request',
|
||||
'Bad request syntax or unsupported method')
|
||||
UNAUTHORIZED = (401, 'Unauthorized',
|
||||
'No permission -- see authorization schemes')
|
||||
PAYMENT_REQUIRED = (402, 'Payment Required',
|
||||
'No payment -- see charging schemes')
|
||||
FORBIDDEN = (403, 'Forbidden',
|
||||
'Request forbidden -- authorization will not help')
|
||||
NOT_FOUND = (404, 'Not Found',
|
||||
'Nothing matches the given URI')
|
||||
METHOD_NOT_ALLOWED = (405, 'Method Not Allowed',
|
||||
'Specified method is invalid for this resource')
|
||||
NOT_ACCEPTABLE = (406, 'Not Acceptable',
|
||||
'URI not available in preferred format')
|
||||
PROXY_AUTHENTICATION_REQUIRED = (407,
|
||||
'Proxy Authentication Required',
|
||||
'You must authenticate with this proxy before proceeding')
|
||||
REQUEST_TIMEOUT = (408, 'Request Timeout',
|
||||
'Request timed out; try again later')
|
||||
CONFLICT = 409, 'Conflict', 'Request conflict'
|
||||
GONE = (410, 'Gone',
|
||||
'URI no longer exists and has been permanently removed')
|
||||
LENGTH_REQUIRED = (411, 'Length Required',
|
||||
'Client must specify Content-Length')
|
||||
PRECONDITION_FAILED = (412, 'Precondition Failed',
|
||||
'Precondition in headers is false')
|
||||
REQUEST_ENTITY_TOO_LARGE = (413, 'Request Entity Too Large',
|
||||
'Entity is too large')
|
||||
REQUEST_URI_TOO_LONG = (414, 'Request-URI Too Long',
|
||||
'URI is too long')
|
||||
UNSUPPORTED_MEDIA_TYPE = (415, 'Unsupported Media Type',
|
||||
'Entity body in unsupported format')
|
||||
REQUEST_RANGE_NOT_SATISFIABLE = (416,
|
||||
'Request Range Not Satisfiable',
|
||||
'Cannot satisfy request range')
|
||||
EXPECTATION_FAILED = (417, 'Expectation Failed',
|
||||
'Expect condition could not be satisfied')
|
||||
IM_A_TEAPOT = 418, 'I\'m a teapot'
|
||||
AUTHENTICATION_TIMEOUT = 419, 'Authentication Timeout'
|
||||
METHOD_FAILURE = 420, 'Method Failure' # Spring framework
|
||||
UNPROCESSABLE_ENTITY = 422, 'Unprocessable Entity'
|
||||
LOCKED = 423, 'Locked'
|
||||
FAILED_DEPENDENCY = 424, 'Failed Dependency'
|
||||
UPGRADE_REQUIRED = 426, 'Upgrade Required'
|
||||
PRECONDITION_REQUIRED = (428, 'Precondition Required',
|
||||
'The origin server requires the request to be conditional')
|
||||
TOO_MANY_REQUESTS = (429, 'Too Many Requests',
|
||||
'The user has sent too many requests in '
|
||||
'a given amount of time ("rate limiting")')
|
||||
REQUEST_HEADER_FIELD_TOO_LARGE = (431,
|
||||
'Request Header Field Too Large',
|
||||
'The server is unwilling to process the request because its header '
|
||||
'fields are too large')
|
||||
LOGIN_TIMEOUT = 440, 'Login Timeout' # microsoft
|
||||
NO_RESPONSE = 444, 'No Response' # nginx
|
||||
RETRY_WITH = 449, 'Retry With' # microsoft
|
||||
BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS = (450,
|
||||
'Blocked By Windows Parental Controls') # microsoft
|
||||
REQUEST_HEADER_TOO_LARGE = 494, 'Request Header Too Large' # nginx
|
||||
CERT_ERROR = 495, 'Cert Error' # nginx
|
||||
NO_CERT = 496, 'No Cert' # nginx
|
||||
HTTP_TO_HTTPS = 497, 'HTTP To HTTPS' # nginx
|
||||
CLIENT_CLOSED_REQUEST = 499, 'Client Closed Request' # nginx
|
||||
|
||||
# server errors
|
||||
INTERNAL_SERVER_ERROR = (500, 'Internal Server Error',
|
||||
'Server got itself in trouble')
|
||||
NOT_IMPLEMENTED = (501, 'Not Implemented',
|
||||
'Server does not support this operation')
|
||||
BAD_GATEWAY = (502, 'Bad Gateway',
|
||||
'Invalid responses from another server/proxy')
|
||||
SERVICE_UNAVAILABLE = (503, 'Service Unavailable',
|
||||
'The server cannot process the request due to a high load')
|
||||
GATEWAY_TIMEOUT = (504, 'Gateway Timeout',
|
||||
'The gateway server did not receive a timely response')
|
||||
HTTP_VERSION_NOT_SUPPORTED = (505, 'HTTP Version Not Supported',
|
||||
'Cannot fulfill request')
|
||||
VARIANT_ALSO_NEGOTIATES = 506, 'Variant Also Negotiates'
|
||||
INSUFFICIENT_STORAGE = 507, 'Insufficient Storage'
|
||||
LOOP_DETECTED = 508, 'Loop Detected'
|
||||
BANDWIDTH_LIMIT_EXCEEDED = 509, 'Bandwidth Limit Exceeded'
|
||||
NOT_EXTENDED = 510, 'Not Extended'
|
||||
NETWORK_AUTHENTICATION_REQUIRED = (511,
|
||||
'Network Authentication Required',
|
||||
'The client needs to authenticate to gain network access')
|
||||
ORIGIN_ERROR = 520, 'Origin Error' # cloudflare
|
||||
WEB_SERVER_IS_DOWN = 521, 'Web Server Is Down' # cloudflare
|
||||
CONNECTON_TIMED_OUT = 522, 'Connection Timed Out' # cloudflare
|
||||
PROXY_DECLINED_REQUEST = 523, 'Proxy Declined Request' # cloudflare
|
||||
A_TIMEOUT_OCCURRED = 524, 'A Timeout Occurred', '' # cloudflare
|
||||
NETWORK_READ_TIMEOUT_ERROR = 598, 'Network Read Timeout Error'
|
||||
NETWORK_CONNECT_TIMEOUT_ERROR = 599, 'Network Connect Timeout Error'
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ Req-sent-unread-response _CS_REQ_SENT <response_class>
|
|||
|
||||
import email.parser
|
||||
import email.message
|
||||
import http
|
||||
import io
|
||||
import os
|
||||
import socket
|
||||
|
|
@ -91,122 +92,13 @@ _CS_IDLE = 'Idle'
|
|||
_CS_REQ_STARTED = 'Request-started'
|
||||
_CS_REQ_SENT = 'Request-sent'
|
||||
|
||||
# status codes
|
||||
# informational
|
||||
CONTINUE = 100
|
||||
SWITCHING_PROTOCOLS = 101
|
||||
PROCESSING = 102
|
||||
|
||||
# successful
|
||||
OK = 200
|
||||
CREATED = 201
|
||||
ACCEPTED = 202
|
||||
NON_AUTHORITATIVE_INFORMATION = 203
|
||||
NO_CONTENT = 204
|
||||
RESET_CONTENT = 205
|
||||
PARTIAL_CONTENT = 206
|
||||
MULTI_STATUS = 207
|
||||
IM_USED = 226
|
||||
|
||||
# redirection
|
||||
MULTIPLE_CHOICES = 300
|
||||
MOVED_PERMANENTLY = 301
|
||||
FOUND = 302
|
||||
SEE_OTHER = 303
|
||||
NOT_MODIFIED = 304
|
||||
USE_PROXY = 305
|
||||
TEMPORARY_REDIRECT = 307
|
||||
|
||||
# client error
|
||||
BAD_REQUEST = 400
|
||||
UNAUTHORIZED = 401
|
||||
PAYMENT_REQUIRED = 402
|
||||
FORBIDDEN = 403
|
||||
NOT_FOUND = 404
|
||||
METHOD_NOT_ALLOWED = 405
|
||||
NOT_ACCEPTABLE = 406
|
||||
PROXY_AUTHENTICATION_REQUIRED = 407
|
||||
REQUEST_TIMEOUT = 408
|
||||
CONFLICT = 409
|
||||
GONE = 410
|
||||
LENGTH_REQUIRED = 411
|
||||
PRECONDITION_FAILED = 412
|
||||
REQUEST_ENTITY_TOO_LARGE = 413
|
||||
REQUEST_URI_TOO_LONG = 414
|
||||
UNSUPPORTED_MEDIA_TYPE = 415
|
||||
REQUESTED_RANGE_NOT_SATISFIABLE = 416
|
||||
EXPECTATION_FAILED = 417
|
||||
UNPROCESSABLE_ENTITY = 422
|
||||
LOCKED = 423
|
||||
FAILED_DEPENDENCY = 424
|
||||
UPGRADE_REQUIRED = 426
|
||||
PRECONDITION_REQUIRED = 428
|
||||
TOO_MANY_REQUESTS = 429
|
||||
REQUEST_HEADER_FIELDS_TOO_LARGE = 431
|
||||
|
||||
# server error
|
||||
INTERNAL_SERVER_ERROR = 500
|
||||
NOT_IMPLEMENTED = 501
|
||||
BAD_GATEWAY = 502
|
||||
SERVICE_UNAVAILABLE = 503
|
||||
GATEWAY_TIMEOUT = 504
|
||||
HTTP_VERSION_NOT_SUPPORTED = 505
|
||||
INSUFFICIENT_STORAGE = 507
|
||||
NOT_EXTENDED = 510
|
||||
NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
# hack to maintain backwards compatibility
|
||||
globals().update(http.HTTPStatus.__members__)
|
||||
|
||||
# another hack to maintain backwards compatibility
|
||||
# Mapping status codes to official W3C names
|
||||
responses = {
|
||||
100: 'Continue',
|
||||
101: 'Switching Protocols',
|
||||
|
||||
200: 'OK',
|
||||
201: 'Created',
|
||||
202: 'Accepted',
|
||||
203: 'Non-Authoritative Information',
|
||||
204: 'No Content',
|
||||
205: 'Reset Content',
|
||||
206: 'Partial Content',
|
||||
|
||||
300: 'Multiple Choices',
|
||||
301: 'Moved Permanently',
|
||||
302: 'Found',
|
||||
303: 'See Other',
|
||||
304: 'Not Modified',
|
||||
305: 'Use Proxy',
|
||||
306: '(Unused)',
|
||||
307: 'Temporary Redirect',
|
||||
|
||||
400: 'Bad Request',
|
||||
401: 'Unauthorized',
|
||||
402: 'Payment Required',
|
||||
403: 'Forbidden',
|
||||
404: 'Not Found',
|
||||
405: 'Method Not Allowed',
|
||||
406: 'Not Acceptable',
|
||||
407: 'Proxy Authentication Required',
|
||||
408: 'Request Timeout',
|
||||
409: 'Conflict',
|
||||
410: 'Gone',
|
||||
411: 'Length Required',
|
||||
412: 'Precondition Failed',
|
||||
413: 'Request Entity Too Large',
|
||||
414: 'Request-URI Too Long',
|
||||
415: 'Unsupported Media Type',
|
||||
416: 'Requested Range Not Satisfiable',
|
||||
417: 'Expectation Failed',
|
||||
428: 'Precondition Required',
|
||||
429: 'Too Many Requests',
|
||||
431: 'Request Header Fields Too Large',
|
||||
|
||||
500: 'Internal Server Error',
|
||||
501: 'Not Implemented',
|
||||
502: 'Bad Gateway',
|
||||
503: 'Service Unavailable',
|
||||
504: 'Gateway Timeout',
|
||||
505: 'HTTP Version Not Supported',
|
||||
511: 'Network Authentication Required',
|
||||
}
|
||||
responses = {v: v.phrase for v in http.HTTPStatus.__members__.values()}
|
||||
|
||||
# maximal amount of data to read at one time in _safe_read
|
||||
MAXAMOUNT = 1048576
|
||||
|
|
@ -878,7 +770,7 @@ class HTTPConnection:
|
|||
response = self.response_class(self.sock, method=self._method)
|
||||
(version, code, message) = response._read_status()
|
||||
|
||||
if code != 200:
|
||||
if code != http.HTTPStatus.OK:
|
||||
self.close()
|
||||
raise OSError("Tunnel connection failed: %d %s" % (code,
|
||||
message.strip()))
|
||||
|
|
|
|||
|
|
@ -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('+', ' ')
|
||||
|
|
|
|||
|
|
@ -196,6 +196,9 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #21793: Added http.HTTPStatus enums (i.e. HTTPStatus.OK,
|
||||
HTTPStatus.NOT_FOUND). Patch by Demian Brecht.
|
||||
|
||||
- Issue #23093: In the io, module allow more operations to work on detached
|
||||
streams.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue