Skip to content

Commit

Permalink
Fixed #30015 -- Clear previously posted data in keep-alive connections.
Browse files Browse the repository at this point in the history
  • Loading branch information
kalekseev committed Dec 18, 2018
1 parent a394289 commit 550072d
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 0 deletions.
19 changes: 19 additions & 0 deletions django/core/servers/basehttp.py
Expand Up @@ -14,6 +14,7 @@
from wsgiref import simple_server

from django.core.exceptions import ImproperlyConfigured
from django.core.handlers.wsgi import LimitedStream
from django.core.wsgi import get_wsgi_application
from django.utils.module_loading import import_string

Expand Down Expand Up @@ -80,6 +81,20 @@ class ThreadedWSGIServer(socketserver.ThreadingMixIn, WSGIServer):
class ServerHandler(simple_server.ServerHandler):
http_version = '1.1'

def __init__(self, stdin, stdout, stderr, environ, **kwargs):
"""
Setup a limited stream, so we can discard unread request data
at the end of the request. Django already uses `LimitedStream`
in `WSGIRequest` but it shouldn't discard the data since the
upstream servers usually do this. Hence we fix this only for
our testserver/runserver.
"""
try:
content_length = int(environ.get('CONTENT_LENGTH'))
except (ValueError, TypeError):
content_length = 0
super().__init__(LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs)

def cleanup_headers(self):
super().cleanup_headers()
# HTTP/1.1 requires us to support persistent connections, so
Expand All @@ -92,6 +107,10 @@ def cleanup_headers(self):
if self.headers.get('Connection') == 'close':
self.request_handler.close_connection = True

def close(self):
super().close()
self.get_stdin()._read_limited()

def handle_error(self):
# Ignore broken pipe errors, otherwise pass on
if not is_broken_pipe_error():
Expand Down
17 changes: 17 additions & 0 deletions tests/servers/tests.py
Expand Up @@ -111,6 +111,23 @@ def test_keep_alive_on_connection_with_content_length(self):
finally:
conn.close()

def test_keep_alive_connection_clears_previous_request_data(self):
conn = HTTPConnection(LiveServerViews.server_thread.host, LiveServerViews.server_thread.port)
try:
conn.request('POST', '/method_view/', b'{}', headers={"Connection": "keep-alive"})
response = conn.getresponse()
self.assertFalse(response.will_close)
self.assertEqual(response.status, 200)
self.assertEqual(response.read(), b'POST')

conn.request('POST', '/method_view/', b'{}', headers={"Connection": "close"})
response = conn.getresponse()
self.assertFalse(response.will_close)
self.assertEqual(response.status, 200)
self.assertEqual(response.read(), b'POST')
finally:
conn.close()

def test_404(self):
with self.assertRaises(HTTPError) as err:
self.urlopen('/')
Expand Down
1 change: 1 addition & 0 deletions tests/servers/urls.py
Expand Up @@ -11,4 +11,5 @@
url(r'^subview_calling_view/$', views.subview_calling_view),
url(r'^subview/$', views.subview),
url(r'^check_model_instance_from_subview/$', views.check_model_instance_from_subview),
url(r'^method_view/$', views.method_view),
]
6 changes: 6 additions & 0 deletions tests/servers/views.py
@@ -1,6 +1,7 @@
from urllib.request import urlopen

from django.http import HttpResponse, StreamingHttpResponse
from django.views.decorators.csrf import csrf_exempt

from .models import Person

Expand Down Expand Up @@ -42,3 +43,8 @@ def check_model_instance_from_subview(request):
pass
with urlopen(request.GET['url'] + '/model_view/') as response:
return HttpResponse('subview calling view: {}'.format(response.read().decode()))


@csrf_exempt
def method_view(request):
return HttpResponse(request.method)

0 comments on commit 550072d

Please sign in to comment.