mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-01 18:21: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
|
||||
|
||||
|
||||
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):
|
||||
def __init__(self, version):
|
||||
self.version = version
|
||||
|
||||
@ -14,6 +14,7 @@ from gunicorn.http.errors import (
|
||||
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion,
|
||||
LimitRequestLine, LimitRequestHeaders,
|
||||
UnsupportedTransferCoding, ObsoleteFolding,
|
||||
ExpectationFailed,
|
||||
)
|
||||
from gunicorn.http.errors import InvalidProxyLine, InvalidProxyHeader, ForbiddenProxyRequest
|
||||
from gunicorn.http.errors import InvalidSchemeHeaders
|
||||
@ -90,6 +91,7 @@ class Message:
|
||||
self.body = None
|
||||
self.scheme = "https" if cfg.is_ssl else "http"
|
||||
self.must_close = False
|
||||
self._expected_100_continue = False
|
||||
|
||||
# set headers limits
|
||||
self.limit_request_fields = cfg.limit_request_fields
|
||||
@ -180,6 +182,21 @@ class Message:
|
||||
if header_length > self.limit_request_field_size > 0:
|
||||
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:
|
||||
secure = value == secure_scheme_headers[name]
|
||||
scheme = "https" if secure else "http"
|
||||
|
||||
@ -117,13 +117,14 @@ def create(req, sock, client, server, cfg):
|
||||
host = None
|
||||
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
|
||||
for hdr_name, hdr_value in req.headers:
|
||||
if hdr_name == "EXPECT":
|
||||
# handle expect
|
||||
if hdr_value.lower() == "100-continue":
|
||||
sock.send(b"HTTP/1.1 100 Continue\r\n\r\n")
|
||||
elif hdr_name == 'HOST':
|
||||
if hdr_name == 'HOST':
|
||||
host = hdr_value
|
||||
elif hdr_name == "SCRIPT_NAME":
|
||||
script_name = hdr_value
|
||||
|
||||
@ -19,7 +19,7 @@ from gunicorn.http.errors import (
|
||||
InvalidProxyLine, InvalidRequestLine,
|
||||
InvalidRequestMethod, InvalidSchemeHeaders,
|
||||
LimitRequestHeaders, LimitRequestLine,
|
||||
UnsupportedTransferCoding,
|
||||
UnsupportedTransferCoding, ExpectationFailed,
|
||||
ConfigurationProblem, ObsoleteFolding,
|
||||
)
|
||||
from gunicorn.http.wsgi import Response, default_environ
|
||||
@ -212,7 +212,7 @@ class Worker:
|
||||
LimitRequestLine, LimitRequestHeaders,
|
||||
InvalidProxyLine, ForbiddenProxyRequest,
|
||||
InvalidSchemeHeaders, UnsupportedTransferCoding,
|
||||
ConfigurationProblem, ObsoleteFolding,
|
||||
ConfigurationProblem, ObsoleteFolding, ExpectationFailed,
|
||||
SSLError,
|
||||
)):
|
||||
|
||||
@ -239,6 +239,10 @@ class Worker:
|
||||
req = exc.req # for access log
|
||||
elif isinstance(exc, LimitRequestLine):
|
||||
mesg = "%s" % str(exc)
|
||||
elif isinstance(exc, ExpectationFailed):
|
||||
reason = "Expectation Failed"
|
||||
mesg = str(exc)
|
||||
status_int = 417
|
||||
elif isinstance(exc, LimitRequestHeaders):
|
||||
reason = "Request Header Fields Too Large"
|
||||
mesg = "Error parsing headers: '%s'" % str(exc)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user