mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-02 10:41:30 +08:00
HTTP/1.0 - ignore Expect: 100-continue
* ignore on HTTP/1.0 (would possibly confuse a client or proxy) * refuse requests with unknown expectations https://datatracker.ietf.org/doc/html/rfc9110#section-10.1.1
This commit is contained in:
parent
f0952e5874
commit
88d503ba1c
@ -47,6 +47,14 @@ class InvalidRequestMethod(ParseException):
|
|||||||
return "Invalid HTTP method: %r" % self.method
|
return "Invalid HTTP method: %r" % self.method
|
||||||
|
|
||||||
|
|
||||||
|
class ExpectationFailed(ParseException):
|
||||||
|
def __init__(self, expect):
|
||||||
|
self.expect = expect
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Unable to comply with expectation: %r" % (self.expect, )
|
||||||
|
|
||||||
|
|
||||||
class InvalidHTTPVersion(ParseException):
|
class InvalidHTTPVersion(ParseException):
|
||||||
def __init__(self, version):
|
def __init__(self, version):
|
||||||
self.version = version
|
self.version = version
|
||||||
|
|||||||
@ -14,6 +14,7 @@ from gunicorn.http.errors import (
|
|||||||
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion,
|
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion,
|
||||||
LimitRequestLine, LimitRequestHeaders,
|
LimitRequestLine, LimitRequestHeaders,
|
||||||
UnsupportedTransferCoding, ObsoleteFolding,
|
UnsupportedTransferCoding, ObsoleteFolding,
|
||||||
|
ExpectationFailed,
|
||||||
)
|
)
|
||||||
from gunicorn.http.errors import InvalidProxyLine, InvalidProxyHeader, ForbiddenProxyRequest
|
from gunicorn.http.errors import InvalidProxyLine, InvalidProxyHeader, ForbiddenProxyRequest
|
||||||
from gunicorn.http.errors import InvalidSchemeHeaders
|
from gunicorn.http.errors import InvalidSchemeHeaders
|
||||||
@ -90,6 +91,7 @@ class Message:
|
|||||||
self.body = None
|
self.body = None
|
||||||
self.scheme = "https" if cfg.is_ssl else "http"
|
self.scheme = "https" if cfg.is_ssl else "http"
|
||||||
self.must_close = False
|
self.must_close = False
|
||||||
|
self._expected_100_continue = False
|
||||||
|
|
||||||
# set headers limits
|
# set headers limits
|
||||||
self.limit_request_fields = cfg.limit_request_fields
|
self.limit_request_fields = cfg.limit_request_fields
|
||||||
@ -180,6 +182,21 @@ class Message:
|
|||||||
if header_length > self.limit_request_field_size > 0:
|
if header_length > self.limit_request_field_size > 0:
|
||||||
raise LimitRequestHeaders("limit request headers fields size")
|
raise LimitRequestHeaders("limit request headers fields size")
|
||||||
|
|
||||||
|
if not from_trailer and name == "EXPECT":
|
||||||
|
# https://datatracker.ietf.org/doc/html/rfc9110#section-10.1.1
|
||||||
|
# "The Expect field value is case-insensitive."
|
||||||
|
if value.lower() == "100-continue":
|
||||||
|
if self.version < (1, 1):
|
||||||
|
# https://datatracker.ietf.org/doc/html/rfc9110#section-10.1.1-12
|
||||||
|
# "A server that receives a 100-continue expectation
|
||||||
|
# in an HTTP/1.0 request MUST ignore that expectation."
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self._expected_100_continue = True
|
||||||
|
# N.B. understood but ignored expect header does not return 417
|
||||||
|
else:
|
||||||
|
raise ExpectationFailed(value)
|
||||||
|
|
||||||
if name in secure_scheme_headers:
|
if name in secure_scheme_headers:
|
||||||
secure = value == secure_scheme_headers[name]
|
secure = value == secure_scheme_headers[name]
|
||||||
scheme = "https" if secure else "http"
|
scheme = "https" if secure else "http"
|
||||||
|
|||||||
@ -117,13 +117,14 @@ def create(req, sock, client, server, cfg):
|
|||||||
host = None
|
host = None
|
||||||
script_name = os.environ.get("SCRIPT_NAME", "")
|
script_name = os.environ.get("SCRIPT_NAME", "")
|
||||||
|
|
||||||
|
if req._expected_100_continue:
|
||||||
|
sock.send(b"HTTP/1.1 100 Continue\r\n\r\n")
|
||||||
|
# rfc9112: Expect MUST be forwarded if the request is forwarded
|
||||||
|
# N.B. gunicorn just sends at most one - application might send another
|
||||||
|
|
||||||
# add the headers to the environ
|
# add the headers to the environ
|
||||||
for hdr_name, hdr_value in req.headers:
|
for hdr_name, hdr_value in req.headers:
|
||||||
if hdr_name == "EXPECT":
|
if hdr_name == 'HOST':
|
||||||
# handle expect
|
|
||||||
if hdr_value.lower() == "100-continue":
|
|
||||||
sock.send(b"HTTP/1.1 100 Continue\r\n\r\n")
|
|
||||||
elif hdr_name == 'HOST':
|
|
||||||
host = hdr_value
|
host = hdr_value
|
||||||
elif hdr_name == "SCRIPT_NAME":
|
elif hdr_name == "SCRIPT_NAME":
|
||||||
script_name = hdr_value
|
script_name = hdr_value
|
||||||
|
|||||||
@ -19,7 +19,7 @@ from gunicorn.http.errors import (
|
|||||||
InvalidProxyLine, InvalidRequestLine,
|
InvalidProxyLine, InvalidRequestLine,
|
||||||
InvalidRequestMethod, InvalidSchemeHeaders,
|
InvalidRequestMethod, InvalidSchemeHeaders,
|
||||||
LimitRequestHeaders, LimitRequestLine,
|
LimitRequestHeaders, LimitRequestLine,
|
||||||
UnsupportedTransferCoding,
|
UnsupportedTransferCoding, ExpectationFailed,
|
||||||
ConfigurationProblem, ObsoleteFolding,
|
ConfigurationProblem, ObsoleteFolding,
|
||||||
)
|
)
|
||||||
from gunicorn.http.wsgi import Response, default_environ
|
from gunicorn.http.wsgi import Response, default_environ
|
||||||
@ -212,7 +212,7 @@ class Worker:
|
|||||||
LimitRequestLine, LimitRequestHeaders,
|
LimitRequestLine, LimitRequestHeaders,
|
||||||
InvalidProxyLine, ForbiddenProxyRequest,
|
InvalidProxyLine, ForbiddenProxyRequest,
|
||||||
InvalidSchemeHeaders, UnsupportedTransferCoding,
|
InvalidSchemeHeaders, UnsupportedTransferCoding,
|
||||||
ConfigurationProblem, ObsoleteFolding,
|
ConfigurationProblem, ObsoleteFolding, ExpectationFailed,
|
||||||
SSLError,
|
SSLError,
|
||||||
)):
|
)):
|
||||||
|
|
||||||
@ -239,6 +239,10 @@ class Worker:
|
|||||||
req = exc.req # for access log
|
req = exc.req # for access log
|
||||||
elif isinstance(exc, LimitRequestLine):
|
elif isinstance(exc, LimitRequestLine):
|
||||||
mesg = "%s" % str(exc)
|
mesg = "%s" % str(exc)
|
||||||
|
elif isinstance(exc, ExpectationFailed):
|
||||||
|
reason = "Expectation Failed"
|
||||||
|
mesg = str(exc)
|
||||||
|
status_int = 417
|
||||||
elif isinstance(exc, LimitRequestHeaders):
|
elif isinstance(exc, LimitRequestHeaders):
|
||||||
reason = "Request Header Fields Too Large"
|
reason = "Request Header Fields Too Large"
|
||||||
mesg = "Error parsing headers: '%s'" % str(exc)
|
mesg = "Error parsing headers: '%s'" % str(exc)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user