Repository: tornadoweb/tornado

Issue:
Incorrect response for invalid Content-Length header
Tornado responds with status 500 when gets non decimal characters in Content-Length header.

Environment:
Ubuntu 14.04
Python 3.4.3
Tornado 4.3 

Logged error message:

```
[ERROR] tornado.general: Uncaught exception
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/tornado/http1connection.py", line 706, in _server_request_loop
    ret = yield conn.read_response(request_delegate)
  File "/usr/local/lib/python3.4/dist-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python3.4/dist-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
  File "/usr/local/lib/python3.4/dist-packages/tornado/gen.py", line 1017, in run
    yielded = self.gen.send(value)
  File "/usr/local/lib/python3.4/dist-packages/tornado/http1connection.py", line 219, in _read_message
    start_line.code if self.is_client else 0, headers, delegate)
  File "/usr/local/lib/python3.4/dist-packages/tornado/http1connection.py", line 534, in _read_body
    content_length = int(headers["Content-Length"])
ValueError: invalid literal for int() with base 10: '\x8d\x00\x00\x00'
```

Incorrect response for invalid Content-Length header
Tornado responds with status 500 when gets non decimal characters in Content-Length header.

Environment:
Ubuntu 14.04
Python 3.4.3
Tornado 4.3 

Logged error message:

```
[ERROR] tornado.general: Uncaught exception
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/tornado/http1connection.py", line 706, in _server_request_loop
    ret = yield conn.read_response(request_delegate)
  File "/usr/local/lib/python3.4/dist-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python3.4/dist-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
  File "/usr/local/lib/python3.4/dist-packages/tornado/gen.py", line 1017, in run
    yielded = self.gen.send(value)
  File "/usr/local/lib/python3.4/dist-packages/tornado/http1connection.py", line 219, in _read_message
    start_line.code if self.is_client else 0, headers, delegate)
  File "/usr/local/lib/python3.4/dist-packages/tornado/http1connection.py", line 534, in _read_body
    content_length = int(headers["Content-Length"])
ValueError: invalid literal for int() with base 10: '\x8d\x00\x00\x00'
```



Discussion:

Resolving patch:
diff --git a/tornado/http1connection.py b/tornado/http1connection.py
--- a/tornado/http1connection.py
+++ b/tornado/http1connection.py
@@ -540,7 +540,13 @@ def _read_body(self, code, headers, delegate):
                         "Multiple unequal Content-Lengths: %r" %
                         headers["Content-Length"])
                 headers["Content-Length"] = pieces[0]
-            content_length = int(headers["Content-Length"])
+
+            try:
+                content_length = int(headers["Content-Length"])
+            except ValueError:
+                # Handles non-integer Content-Length value.
+                raise httputil.HTTPInputError(
+                    "Only integer Content-Length is allowed: %s" % headers["Content-Length"])
 
             if content_length > self._max_body_size:
                 raise httputil.HTTPInputError("Content-Length too long")


Test patch:
diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py
--- a/tornado/test/httpserver_test.py
+++ b/tornado/test/httpserver_test.py
@@ -441,6 +441,18 @@ def test_chunked_request_body(self):
         headers, response = self.wait()
         self.assertEqual(json_decode(response), {u'foo': [u'bar']})
 
+    def test_invalid_content_length(self):
+        with ExpectLog(gen_log, '.*Only integer Content-Length is allowed'):
+            self.stream.write(b"""\
+POST /echo HTTP/1.1
+Content-Length: foo
+
+bar
+
+""".replace(b"\n", b"\r\n"))
+            self.stream.read_until_close(self.stop)
+            self.wait()
+
 
 class XHeaderTest(HandlerBaseTestCase):
     class Handler(RequestHandler):