mirror of
https://github.com/django/django.git
synced 2025-10-24 06:06:09 +00:00
Fixed keep-alive support in manage.py runserver.
Ticket #25619 changed the default protocol to HTTP/1.1 but did not properly implement keep-alive. As a "fix" keep-alive was disabled in ticket #28440 to prevent clients from hanging (they expect the server to send more data if the connection is not closed and there is no content length set). The combination of those two fixes resulted in yet another problem: HTTP/1.1 by default allows a client to assume that keep-alive is supported unless the server disables it via 'Connection: close' -- see RFC2616 8.1.2.1 for details on persistent connection negotiation. Now if the client receives a response from Django without 'Connection: close' and immediately sends a new request (on the same tcp connection) before our server closes the tcp connection, it will error out at some point because the connection does get closed a few milli seconds later. This patch fixes the mentioned issues by always sending 'Connection: close' if we cannot determine a content length. The code is inefficient in the sense that it does not allow for persistent connections when chunked responses are used, but that should not really cause any problems (Django does not generate those) and it only affects the development server anyways. Refs #25619, #28440.
This commit is contained in:
committed by
Florian Apolloner
parent
1f726311d1
commit
934acf1126
@@ -74,12 +74,24 @@ class WSGIServer(simple_server.WSGIServer):
|
||||
|
||||
class ThreadedWSGIServer(socketserver.ThreadingMixIn, WSGIServer):
|
||||
"""A threaded version of the WSGIServer"""
|
||||
pass
|
||||
daemon_threads = True
|
||||
|
||||
|
||||
class ServerHandler(simple_server.ServerHandler):
|
||||
http_version = '1.1'
|
||||
|
||||
def cleanup_headers(self):
|
||||
super().cleanup_headers()
|
||||
# HTTP/1.1 requires us to support persistent connections, so
|
||||
# explicitly send close if we do not know the content length to
|
||||
# prevent clients from reusing the connection.
|
||||
if 'Content-Length' not in self.headers:
|
||||
self.headers['Connection'] = 'close'
|
||||
# Mark the connection for closing if we set it as such above or
|
||||
# if the application sent the header.
|
||||
if self.headers.get('Connection') == 'close':
|
||||
self.request_handler.close_connection = True
|
||||
|
||||
def handle_error(self):
|
||||
# Ignore broken pipe errors, otherwise pass on
|
||||
if not is_broken_pipe_error():
|
||||
@@ -135,6 +147,16 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler):
|
||||
return super().get_environ()
|
||||
|
||||
def handle(self):
|
||||
self.close_connection = True
|
||||
self.handle_one_request()
|
||||
while not self.close_connection:
|
||||
self.handle_one_request()
|
||||
try:
|
||||
self.connection.shutdown(socket.SHUT_WR)
|
||||
except (socket.error, AttributeError):
|
||||
pass
|
||||
|
||||
def handle_one_request(self):
|
||||
"""Copy of WSGIRequestHandler.handle() but with different ServerHandler"""
|
||||
self.raw_requestline = self.rfile.readline(65537)
|
||||
if len(self.raw_requestline) > 65536:
|
||||
@@ -150,7 +172,7 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler):
|
||||
handler = ServerHandler(
|
||||
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
|
||||
)
|
||||
handler.request_handler = self # backpointer for logging
|
||||
handler.request_handler = self # backpointer for logging & connection closing
|
||||
handler.run(self.server.get_app())
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user