From c8c0a94bd4922a544233a3030a2cfc2807a31c55 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Wed, 17 Feb 2010 09:16:01 -0800 Subject: [PATCH 1/5] Fix for #11, with chunked encoding, closing chunk is sometimes sent twice --- eventlet/wsgi.py | 11 +++++++++-- tests/wsgi_test.py | 26 +++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 4bd7345..9d39ead 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -270,7 +270,11 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): if use_chunked[0]: ## Write the chunked encoding - towrite.append("%x\r\n%s\r\n" % (len(data), data)) + if data: + towrite.append("%x\r\n%s\r\n" % (len(data), data)) + else: + # last-chunk format + towrite.append("0\r\n") else: towrite.append(data) try: @@ -315,16 +319,19 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): headers_set[1].append(('Content-Length', str(sum(map(len, result))))) towrite = [] towrite_size = 0 + just_written_size = 0 for data in result: towrite.append(data) towrite_size += len(data) if towrite_size >= self.minimum_chunk_size: write(''.join(towrite)) towrite = [] + just_written_size = towrite_size towrite_size = 0 if towrite: + just_written_size = towrite_size write(''.join(towrite)) - if not headers_sent or use_chunked[0]: + if not headers_sent or (use_chunked[0] and just_written_size): write('') except Exception, e: self.close_connection = 1 diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index b8a48e5..df6e5b7 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -328,7 +328,7 @@ class TestHttpd(LimitedTestCase): while chunklen: chunks += 1 chunk = fd.read(chunklen) - fd.readline() + fd.readline() # CRLF chunklen = int(fd.readline(), 16) self.assert_(chunks > 1) @@ -768,5 +768,29 @@ class TestHttpd(LimitedTestCase): pass # TODO: should test with OpenSSL greenthread.kill(g) + def test_zero_length_chunked_response(self): + def zero_chunked_app(env, start_response): + start_response('200 OK', [('Content-type', 'text/plain')]) + yield "" + + self.site.application = zero_chunked_app + sock = api.connect_tcp( + ('localhost', self.port)) + + fd = sock.makefile() + fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n') + fd.flush() + response = fd.read().split('\r\n') + headers = [] + while True: + h = response.pop(0) + headers.append(h) + if h == '': + break + self.assert_('Transfer-Encoding: chunked' in ''.join(headers)) + # should only be one chunk of zero size + self.assertEqual(response, ['0', '']) + + if __name__ == '__main__': main() From f857a2de981f14fdc81632edaa06cd79c662c4a9 Mon Sep 17 00:00:00 2001 From: Patrick Carlisle Date: Wed, 17 Feb 2010 10:21:00 -0800 Subject: [PATCH 2/5] Additional fix to upstream #11 - add missing CRLF In addition to a CRLF terminating a chunk, there must be one to terminate the chunked-body after the final chunk. ref: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 --- eventlet/wsgi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 9d39ead..4149308 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -274,7 +274,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): towrite.append("%x\r\n%s\r\n" % (len(data), data)) else: # last-chunk format - towrite.append("0\r\n") + towrite.append("0\r\n\r\n") else: towrite.append(data) try: From 6ab203867b19fcbac6f6c1ef6c56475b94bdcd3d Mon Sep 17 00:00:00 2001 From: Patrick Carlisle Date: Wed, 17 Feb 2010 10:43:21 -0800 Subject: [PATCH 3/5] Update tests for Chunked-Body closing CRLF. --- tests/wsgi_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index df6e5b7..168dbd1 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -331,6 +331,9 @@ class TestHttpd(LimitedTestCase): fd.readline() # CRLF chunklen = int(fd.readline(), 16) self.assert_(chunks > 1) + response = fd.read() + # Require a CRLF to close the message body + self.assertEqual(response, '\r\n') def test_012_ssl_server(self): def wsgi_app(environ, start_response): @@ -788,8 +791,9 @@ class TestHttpd(LimitedTestCase): if h == '': break self.assert_('Transfer-Encoding: chunked' in ''.join(headers)) - # should only be one chunk of zero size - self.assertEqual(response, ['0', '']) + # should only be one chunk of zero size with two blank lines + # (one terminates the chunk, one terminates the body) + self.assertEqual(response, ['0', '', '']) if __name__ == '__main__': From 116d0bbe81885025f23f5d61b8bd5ed27ff7b8e1 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Wed, 17 Feb 2010 13:49:45 -0800 Subject: [PATCH 4/5] Removed unnecessary if; if data is '', the format string will do the right thing. --- eventlet/wsgi.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 4149308..1eebd7e 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -270,11 +270,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): if use_chunked[0]: ## Write the chunked encoding - if data: - towrite.append("%x\r\n%s\r\n" % (len(data), data)) - else: - # last-chunk format - towrite.append("0\r\n\r\n") + towrite.append("%x\r\n%s\r\n" % (len(data), data)) else: towrite.append(data) try: From 41f53ca9476eaed8575232a76bb1b8653d83b21a Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Wed, 17 Feb 2010 13:53:49 -0800 Subject: [PATCH 5/5] Attribution --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 9223227..43fce5c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,6 +11,7 @@ Contributors * Gregory Holt * Chet Murthy * radix +* Patrick Carlisle Linden Lab Contributors -----------------------