diff options
| author | Jakub Stasiak <jakub@stasiak.at> | 2016-02-09 02:24:47 +0100 |
|---|---|---|
| committer | Jakub Stasiak <jakub@stasiak.at> | 2016-02-09 11:20:05 +0100 |
| commit | b7380fdc70fde26777f4f141da3f01570e870cac (patch) | |
| tree | de9292ca35265f717b399a3ee09f4de2bc46b6e3 /tests/wsgi_test.py | |
| parent | 8a4a1212b2097c6ecca96c81dc355388583ac45d (diff) | |
| download | eventlet-partial-write-fix-2.tar.gz | |
wsgi: Fix handling partial writes on Python 3partial-write-fix-2
Closes https://github.com/eventlet/eventlet/issues/295 (in the wsgi
module we use a custom writelines implementation now).
Those write() calls might write only part of the data (and even if they
don't - it's more readable to make sure all data is written
explicitly).
I changed the test code so that the write() implementation returns the
number of characters logged and it cooperates nicely with writeall()
now.
Diffstat (limited to 'tests/wsgi_test.py')
| -rw-r--r-- | tests/wsgi_test.py | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index 50091eb..ef55cd9 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -10,6 +10,8 @@ import tempfile import traceback import unittest +from nose.tools import eq_ + import eventlet from eventlet import debug from eventlet import event @@ -444,6 +446,61 @@ class TestHttpd(_TestBase): # Require a CRLF to close the message body self.assertEqual(response, b'\r\n') + def test_partial_writes_are_handled(self): + # The bug was caused by the default writelines() implementaiton + # (used by the wsgi module) which doesn't check if write() + # successfully completed sending *all* data therefore data could be + # lost and the client could be left hanging forever. + # + # This test additionally ensures that plain write() calls in the wsgi + # are also correct now (replaced with writeare also correct now (replaced with writeall()). + # + # Eventlet issue: "Python 3: wsgi doesn't handle correctly partial + # write of socket send() when using writelines()", + # https://github.com/eventlet/eventlet/issues/295 + # + # Related CPython issue: "Raw I/O writelines() broken", + # http://bugs.python.org/issue26292 + + # Custom accept() and send() in order to simulate a connection that + # only sends one byte at a time so that any code that doesn't handle + # partial writes correctly has to fail. + listen_socket = eventlet.listen(('localhost', 0)) + original_accept = listen_socket.accept + + def accept(): + connection, address = original_accept() + original_send = connection.send + + def send(b, *args): + if b: + b = b[0:1] + return original_send(b, *args) + + connection.send = send + return connection, address + + listen_socket.accept = accept + + def application(env, start_response): + # Sending content-length is important here so that the client knows + # exactly how many bytes does it need to wait for. + start_response('200 OK', [('Content-length', 3)]) + yield 'asd' + + self.spawn_server(sock=listen_socket) + self.site.application = application + + sock = eventlet.connect(('localhost', self.port)) + + sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') + + # This would previously hang forever + result = read_http(sock) + + # Just to be sure we actually read what we wanted + eq_(result.body, b'asd') + @tests.skip_if_no_ssl def test_012_ssl_server(self): def wsgi_app(environ, start_response): |
