# have content-length but no bodies)
self._request_start_line = None
self._response_start_line = None
+ self._request_headers = None
# True if we are writing output with chunked encoding.
self._chunking_output = None
# While reading a body with a content-length, this is the
else:
start_line = httputil.parse_request_start_line(start_line)
self._request_start_line = start_line
+ self._request_headers = headers
self._disconnect_on_finish = not self._can_keep_alive(
start_line, headers)
# Applications are discouraged from touching Transfer-Encoding,
# but if they do, leave it alone.
'Transfer-Encoding' not in headers)
+ # If a 1.0 client asked for keep-alive, add the header.
+ if (self._request_start_line.version == 'HTTP/1.0' and
+ (self._request_headers.get('Connection', '').lower()
+ == 'keep-alive')):
+ headers['Connection'] = 'Keep-Alive'
if self._chunking_output:
headers['Transfer-Encoding'] = 'chunked'
if (not self.is_client and
def read_stream_body(stream, callback):
- """Reads an HTTP response from `stream` and runs callback with its body."""
+ """Reads an HTTP response from `stream` and runs callback with its
+ headers and body."""
chunks = []
class Delegate(HTTPMessageDelegate):
+ def headers_received(self, start_line, headers):
+ self.headers = headers
+
def data_received(self, chunk):
chunks.append(chunk)
def finish(self):
- callback(b''.join(chunks))
+ callback((self.headers, b''.join(chunks)))
conn = HTTP1Connection(stream, True)
conn.read_response(Delegate())
[utf8("Content-Length: %d\r\n" % len(body))]) +
b"\r\n" + body)
read_stream_body(stream, self.stop)
- return self.wait()
+ headers, body = self.wait()
+ return body
def test_multipart_form(self):
# Encodings here are tricky: Headers are latin1, bodies can be
""".replace(b"\n", b"\r\n"))
read_stream_body(self.stream, self.stop)
- response = self.wait()
+ headers, response = self.wait()
self.assertEqual(json_decode(response), {u('foo'): [u('bar')]})
return headers
def read_response(self):
- headers = self.read_headers()
- self.stream.read_bytes(int(headers['Content-Length']), self.stop)
+ self.headers = self.read_headers()
+ self.stream.read_bytes(int(self.headers['Content-Length']), self.stop)
body = self.wait()
self.assertEqual(b'Hello world', body)
self.stream.read_until_close(callback=self.stop)
data = self.wait()
self.assertTrue(not data)
+ self.assertTrue('Connection' not in self.headers)
self.close()
def test_http10_keepalive(self):
self.connect()
self.stream.write(b'GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n')
self.read_response()
+ self.assertEqual(self.headers['Connection'], 'Keep-Alive')
self.stream.write(b'GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n')
self.read_response()
+ self.assertEqual(self.headers['Connection'], 'Keep-Alive')
self.close()
def test_pipelined_requests(self):
stream.write(b'PUT /streaming?expected_size=10240 HTTP/1.1\r\n'
b'Content-Length: 10240\r\n\r\n')
stream.write(b'a'*10240)
- response = yield gen.Task(read_stream_body, stream)
+ headers, response = yield gen.Task(read_stream_body, stream)
self.assertEqual(response, b'10240')
# Without the ?expected_size parameter, we get the old default value
stream.write(b'PUT /streaming HTTP/1.1\r\n'
"Date": httputil.format_timestamp(time.time()),
})
self.set_default_headers()
- if (not self.request.supports_http_1_1() and
- getattr(self.request, 'connection', None) and
- not self.request.connection.no_keep_alive):
- conn_header = self.request.headers.get("Connection")
- if conn_header and (conn_header.lower() == "keep-alive"):
- self._headers["Connection"] = "Keep-Alive"
self._write_buffer = []
self._status_code = 200
self._reason = httputil.responses[200]
MIN_LENGTH = 5
def __init__(self, request):
- self._gzipping = request.supports_http_1_1() and \
- "gzip" in request.headers.get("Accept-Encoding", "")
+ self._gzipping = "gzip" in request.headers.get("Accept-Encoding", "")
def _compressible_type(self, ctype):
return ctype.startswith('text/') or ctype in self.CONTENT_TYPES