diff --git a/src/calibre/srv/http.py b/src/calibre/srv/http.py index 51a3ee8a04..0b26f8dd6b 100644 --- a/src/calibre/srv/http.py +++ b/src/calibre/srv/http.py @@ -247,7 +247,10 @@ class ChunkedReader(object): # {{{ raise BadChunkedInput('%s is not a valid chunk size' % reprlib.repr(chunk_size)) if chunk_size + self.bytes_read > self.maxsize: raise MaxSizeExceeded('Request entity too large', self.bytes_read + chunk_size, self.maxsize) - chunk = self.socket_file.read(chunk_size) + try: + chunk = self.socket_file.read(chunk_size) + except socket.timeout: + raise BadChunkedInput('Timed out waiting for chunk of size %d to complete' % chunk_size) if len(chunk) < chunk_size: raise BadChunkedInput('Bad chunked encoding, chunk truncated: %d < %s' % (len(chunk), chunk_size)) if not chunk.endswith(b'\r\n'): diff --git a/src/calibre/srv/tests/base.py b/src/calibre/srv/tests/base.py index 9f534904c9..65d36985cd 100644 --- a/src/calibre/srv/tests/base.py +++ b/src/calibre/srv/tests/base.py @@ -61,8 +61,8 @@ class TestServer(Thread): def __exit__(self, *args): self.loop.stop() - def connect(self): - return httplib.HTTPConnection(self.address[0], self.address[1], strict=True, timeout=0.1) + def connect(self, timeout=0.1): + return httplib.HTTPConnection(self.address[0], self.address[1], strict=True, timeout=timeout) def change_handler(self, handler): from calibre.srv.http import create_http_handler diff --git a/src/calibre/srv/tests/http.py b/src/calibre/srv/tests/http.py index efe527abb5..08dc31da1b 100644 --- a/src/calibre/srv/tests/http.py +++ b/src/calibre/srv/tests/http.py @@ -64,7 +64,7 @@ class TestHTTP(BaseTest): body = 'Requested resource not found' def handler(conn): raise HTTP404(body) - with TestServer(handler, max_header_line_size=100./1024, max_request_body_size=100./(1024*1024)) as server: + with TestServer(handler, timeout=0.1, max_header_line_size=100./1024, max_request_body_size=100./(1024*1024)) as server: # Test 404 conn = server.connect() conn.request('HEAD', '/moose') @@ -109,14 +109,31 @@ class TestHTTP(BaseTest): r = conn.getresponse() self.ae(r.status, httplib.BAD_REQUEST) + conn = server.connect() conn.request('GET', '/test', ('a' * 200)) r = conn.getresponse() self.ae(r.status, httplib.REQUEST_ENTITY_TOO_LARGE) + conn = server.connect() conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) conn.send(b'x\r\nbody\r\n0\r\n\r\n') r = conn.getresponse() self.ae(r.status, httplib.BAD_REQUEST) + self.assertIn(b'not a valid chunk size', r.read()) + + conn = server.connect() + conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) + conn.send(b'3\r\nbody\r\n0\r\n\r\n') + r = conn.getresponse() + self.ae(r.status, httplib.BAD_REQUEST) + self.assertIn(b'!= CRLF', r.read()) + + conn = server.connect(timeout=1) + conn.request('POST', '/test', headers={'Transfer-Encoding': 'chunked'}) + conn.send(b'30\r\nbody\r\n0\r\n\r\n') + r = conn.getresponse() + self.ae(r.status, httplib.BAD_REQUEST) + self.assertIn(b'Timed out waiting for chunk', r.read()) server.log.filter_level = orig_level conn = server.connect()