From b4f0921463956215c9852398df2674262ac7548e Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Sun, 16 Jan 2011 07:31:35 +0000
Subject: [PATCH] Fixed #15018 -- Corrected the handling of LimitedStream under
 one edge case involving size restricted buffers and newlines. Thanks to
 xjdrew for the report, and aaugustin for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15222 bcc190cf-cafb-0310-a4f2-bffc1f526a37
---
 django/core/handlers/wsgi.py            |  5 +++--
 tests/regressiontests/requests/tests.py | 26 +++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index 4e82bc36d7..537154c0cb 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -96,9 +96,10 @@ class LimitedStream(object):
         return result
 
     def readline(self, size=None):
-        while '\n' not in self.buffer or \
-              (size is not None and len(self.buffer) < size):
+        while '\n' not in self.buffer and \
+              (size is None or len(self.buffer) < size):
             if size:
+                # since size is not None here, len(self.buffer) < size
                 chunk = self._read_limited(size - len(self.buffer))
             else:
                 chunk = self._read_limited()
diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py
index b1d80fe30e..a80ee9d571 100644
--- a/tests/regressiontests/requests/tests.py
+++ b/tests/regressiontests/requests/tests.py
@@ -102,15 +102,21 @@ class RequestsTests(unittest.TestCase):
         # Read all of a limited stream
         stream = LimitedStream(StringIO('test'), 2)
         self.assertEqual(stream.read(), 'te')
+        # Reading again returns nothing.
+        self.assertEqual(stream.read(), '')
 
         # Read a number of characters greater than the stream has to offer
         stream = LimitedStream(StringIO('test'), 2)
         self.assertEqual(stream.read(5), 'te')
+        # Reading again returns nothing.
+        self.assertEqual(stream.readline(5), '')
 
         # Read sequentially from a stream
         stream = LimitedStream(StringIO('12345678'), 8)
         self.assertEqual(stream.read(5), '12345')
         self.assertEqual(stream.read(5), '678')
+        # Reading again returns nothing.
+        self.assertEqual(stream.readline(5), '')
 
         # Read lines from a stream
         stream = LimitedStream(StringIO('1234\n5678\nabcd\nefgh\nijkl'), 24)
@@ -129,6 +135,26 @@ class RequestsTests(unittest.TestCase):
         # Read everything else.
         self.assertEqual(stream.readline(), 'ijkl')
 
+        # Regression for #15018
+        # If a stream contains a newline, but the provided length
+        # is less than the number of provided characters, the newline
+        # doesn't reset the available character count
+        stream = LimitedStream(StringIO('1234\nabcdef'), 9)
+        self.assertEqual(stream.readline(10), '1234\n')
+        self.assertEqual(stream.readline(3), 'abc')
+        # Now expire the available characters
+        self.assertEqual(stream.readline(3), 'd')
+        # Reading again returns nothing.
+        self.assertEqual(stream.readline(2), '')
+
+        # Same test, but with read, not readline.
+        stream = LimitedStream(StringIO('1234\nabcdef'), 9)
+        self.assertEqual(stream.read(6), '1234\na')
+        self.assertEqual(stream.read(2), 'bc')
+        self.assertEqual(stream.read(2), 'd')
+        self.assertEqual(stream.read(2), '')
+        self.assertEqual(stream.read(), '')
+
     def test_stream(self):
         request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')})
         self.assertEqual(request.read(), 'name=value')