wsgi: improved request body discard

- skip request body discarding when connection was to be closed anyway
- handle ChunkReadError while discarding, write to log, close connection

https://github.com/eventlet/eventlet/issues/27
https://github.com/eventlet/eventlet/issues/242
This commit is contained in:
Sergey Shepelev
2015-09-07 04:23:47 +03:00
committed by Thomas Goirand
parent d7caa5ee62
commit 6e82358e95
2 changed files with 26 additions and 22 deletions

View File

@@ -118,14 +118,12 @@ class Input(object):
self.chunk_length = -1
def _do_read(self, reader, length=None):
if self.wfile is not None and \
not self.is_hundred_continue_response_sent:
if self.wfile is not None and not self.is_hundred_continue_response_sent:
# 100 Continue response
self.send_hundred_continue_response()
self.is_hundred_continue_response_sent = True
if length is None and self.content_length is not None:
length = self.content_length - self.position
if length and length > self.content_length - self.position:
if (self.content_length is not None) and (
length is None or length > self.content_length - self.position):
length = self.content_length - self.position
if not length:
return b''
@@ -137,8 +135,7 @@ class Input(object):
return read
def _chunked_read(self, rfile, length=None, use_readline=False):
if self.wfile is not None and \
not self.is_hundred_continue_response_sent:
if self.wfile is not None and not self.is_hundred_continue_response_sent:
# 100 Continue response
self.send_hundred_continue_response()
self.is_hundred_continue_response_sent = True
@@ -223,6 +220,10 @@ class Input(object):
for key, value in headers]
self.hundred_continue_headers = headers
def discard(self, buffer_size=16 << 10):
while self.read(buffer_size):
pass
class HeaderLineTooLong(Exception):
pass
@@ -245,6 +246,9 @@ class LoggerFileWrapper(object):
self.log = log
self._debug = debug
def error(self, msg, *args, **kwargs):
self.write(msg, *args)
def info(self, msg, *args, **kwargs):
self.write(msg, *args)
@@ -503,16 +507,20 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
finally:
if hasattr(result, 'close'):
result.close()
if (self.environ['eventlet.input'].chunked_input or
self.environ['eventlet.input'].position
< (self.environ['eventlet.input'].content_length or 0)):
request_input = self.environ['eventlet.input']
if (request_input.chunked_input or
request_input.position < (request_input.content_length or 0)):
# Read and discard body if there was no pending 100-continue
if not self.environ['eventlet.input'].wfile:
# NOTE: MINIMUM_CHUNK_SIZE is used here for purpose different than chunking.
# We use it only cause it's at hand and has reasonable value in terms of
# emptying the buffer.
while self.environ['eventlet.input'].read(MINIMUM_CHUNK_SIZE):
pass
if not request_input.wfile and self.close_connection == 0:
try:
request_input.discard()
except ChunkReadError as e:
self.close_connection = 1
self.server.log.error((
'chunked encoding error while discarding request body.'
+ ' ip={0} request="{1}" error="{2}"').format(
self.get_client_ip(), self.requestline, e,
))
finish = time.time()
for hook, args, kwargs in self.environ['eventlet.posthooks']:

View File

@@ -1638,7 +1638,6 @@ class ProxiedIterableAlreadyHandledTest(IterableAlreadyHandledTest):
class TestChunkedInput(_TestBase):
dirt = ""
validator = None
def application(self, env, start_response):
@@ -1687,16 +1686,13 @@ class TestChunkedInput(_TestBase):
self.site = Site()
self.site.application = self.application
def chunk_encode(self, chunks, dirt=None):
if dirt is None:
dirt = self.dirt
def chunk_encode(self, chunks, dirt=""):
b = ""
for c in chunks:
b += "%x%s\r\n%s\r\n" % (len(c), dirt, c)
return b
def body(self, dirt=None):
def body(self, dirt=""):
return self.chunk_encode(["this", " is ", "chunked", "\nline",
" 2", "\n", "line3", ""], dirt=dirt)