Fixed #30619 -- Made runserver --nothreading use single threaded WSGIServer.

Browsers often use multiple connections with Connection: keep-alive.
If --nothreading is specified, the WSGI server cannot accept new
connections until the old connection is closed, causing hangs.

Force Connection: close when --nothreading option is used.
This commit is contained in:
atsuo ishimoto 2019-07-07 00:43:59 +09:00 committed by Mariusz Felisiak
parent 00d4e6f8b5
commit a9c6ab0356
2 changed files with 40 additions and 0 deletions

View File

@ -101,6 +101,9 @@ class ServerHandler(simple_server.ServerHandler):
# connection.
if 'Content-Length' not in self.headers:
self.headers['Connection'] = 'close'
# Persistent connections require threading server.
elif not isinstance(self.request_handler.server, socketserver.ThreadingMixIn):
self.headers['Connection'] = 'close'
# Mark the connection for closing if it's set as such above or if the
# application sent the header.
if self.headers.get('Connection') == 'close':

View File

@ -9,7 +9,9 @@ from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import urlopen
from django.core.servers.basehttp import WSGIServer
from django.test import LiveServerTestCase, override_settings
from django.test.testcases import LiveServerThread, QuietWSGIRequestHandler
from .models import Person
@ -50,6 +52,15 @@ class LiveServerAddress(LiveServerBase):
self.assertEqual(self.live_server_url_test[0], self.live_server_url)
class LiveServerSingleThread(LiveServerThread):
def _create_server(self):
return WSGIServer((self.host, self.port), QuietWSGIRequestHandler, allow_reuse_address=False)
class SingleThreadLiveServerTestCase(LiveServerTestCase):
server_thread_class = LiveServerSingleThread
class LiveServerViews(LiveServerBase):
def test_protocol(self):
"""Launched server serves with HTTP 1.1."""
@ -162,6 +173,32 @@ class LiveServerViews(LiveServerBase):
self.assertIn(b"QUERY_STRING: 'q=%D1%82%D0%B5%D1%81%D1%82'", f.read())
@override_settings(ROOT_URLCONF='servers.urls')
class SingleTreadLiveServerViews(SingleThreadLiveServerTestCase):
available_apps = ['servers']
def test_closes_connection_with_content_length(self):
"""
Contrast to
LiveServerViews.test_keep_alive_on_connection_with_content_length().
Persistent connections require threading server.
"""
conn = HTTPConnection(
SingleTreadLiveServerViews.server_thread.host,
SingleTreadLiveServerViews.server_thread.port,
timeout=1,
)
try:
conn.request('GET', '/example_view/', headers={'Connection': 'keep-alive'})
response = conn.getresponse()
self.assertTrue(response.will_close)
self.assertEqual(response.read(), b'example view')
self.assertEqual(response.status, 200)
self.assertEqual(response.getheader('Connection'), 'close')
finally:
conn.close()
class LiveServerDatabase(LiveServerBase):
def test_fixtures_loaded(self):