Add python-eventlet package to MOS 9.0 repository
[packages/trusty/python-eventlet.git] / python-eventlet / tests / wsgi_test.py
index d5cea188f212505f28bc55b495b7a44a13b70019..37c425077a8101df931a479a78ab8c0d4373740d 100644 (file)
@@ -2,25 +2,25 @@ import cgi
 import collections
 import errno
 import os
+import shutil
 import signal
 import socket
 import sys
+import tempfile
 import traceback
 import unittest
 
 import eventlet
 from eventlet import debug
 from eventlet import event
-from eventlet.green import socket as greensocket
-from eventlet.green import ssl
-from eventlet.green import subprocess
 from eventlet import greenio
 from eventlet import greenthread
 from eventlet import support
-from eventlet.support import bytes_to_str, capture_stderr, six
 from eventlet import tpool
 from eventlet import wsgi
-
+from eventlet.green import socket as greensocket
+from eventlet.green import ssl
+from eventlet.support import bytes_to_str, capture_stderr, six
 import tests
 
 
@@ -136,24 +136,13 @@ class IterableSite(Site):
 CONTENT_LENGTH = 'content-length'
 
 
-"""
-HTTP/1.1 200 OK
-Date: foo
-Content-length: 11
-
-hello world
-"""
-
-
-def recvall(socket_):
+def recvall(sock):
     result = b''
     while True:
-        chunk = socket_.recv()
-        result += chunk
+        chunk = sock.recv(16 << 10)
         if chunk == b'':
-            break
-
-    return result
+            return result
+        result += chunk
 
 
 class ConnectionClosed(Exception):
@@ -244,7 +233,7 @@ class _TestBase(tests.LimitedTestCase):
         """Spawns a new wsgi server with the given arguments using
         :meth:`spawn_thread`.
 
-        Sets self.port to the port of the server
+        Sets `self.server_addr` to (host, port) tuple suitable for `socket.connect`.
         """
         new_kwargs = dict(max_size=128,
                           log=self.logfile,
@@ -254,7 +243,7 @@ class _TestBase(tests.LimitedTestCase):
         if 'sock' not in new_kwargs:
             new_kwargs['sock'] = eventlet.listen(('localhost', 0))
 
-        self.port = new_kwargs['sock'].getsockname()[1]
+        self.server_addr = new_kwargs['sock'].getsockname()
         self.spawn_thread(wsgi.server, **new_kwargs)
 
     def spawn_thread(self, target, **kwargs):
@@ -278,72 +267,37 @@ class TestHttpd(_TestBase):
         self.site = Site()
 
     def test_001_server(self):
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
-        fd.flush()
-        result = fd.read()
-        fd.close()
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
+        result = recvall(sock)
         # The server responds with the maximum version it supports
         assert result.startswith(b'HTTP'), result
         assert result.endswith(b'hello world'), result
 
     def test_002_keepalive(self):
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('wb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         read_http(sock)
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         read_http(sock)
-        fd.close()
-        sock.close()
-
-    def test_003_passing_non_int_to_read(self):
-        # This should go in greenio_test
-        sock = eventlet.connect(
-            ('localhost', self.port))
-
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
-        cancel = eventlet.Timeout(1, RuntimeError)
-        self.assertRaises(TypeError, fd.read, "This shouldn't work")
-        cancel.cancel()
-        fd.close()
 
     def test_004_close_keepalive(self):
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('wb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
-        read_http(sock)
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
-        read_http(sock)
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
+        result1 = read_http(sock)
+        assert result1.status == 'HTTP/1.1 200 OK'
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
+        result2 = read_http(sock)
+        assert result2.status == 'HTTP/1.1 200 OK'
+        assert result2.headers_lower['connection'] == 'close'
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         self.assertRaises(ConnectionClosed, read_http, sock)
-        fd.close()
-
-    @tests.skipped
-    def test_005_run_apachebench(self):
-        url = 'http://localhost:12346/'
-        # ab is apachebench
-        subprocess.call(
-            [tests.find_command('ab'), '-c', '64', '-n', '1024', '-k', url],
-            stdout=subprocess.PIPE)
 
     def test_006_reject_long_urls(self):
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         path_parts = []
         for ii in range(3000):
             path_parts.append('path')
@@ -368,65 +322,48 @@ class TestHttpd(_TestBase):
             return [six.b('a is %s, body is %s' % (a, body))]
 
         self.site.application = new_app
-        sock = eventlet.connect(
-            ('localhost', self.port))
-        request = '\r\n'.join((
-            'POST / HTTP/1.0',
-            'Host: localhost',
-            'Content-Length: 3',
-            '',
-            'a=a'))
-        fd = sock.makefile('wb')
-        fd.write(request.encode())
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        request = b'\r\n'.join((
+            b'POST / HTTP/1.0',
+            b'Host: localhost',
+            b'Content-Length: 3',
+            b'',
+            b'a=a'))
+        sock.sendall(request)
 
         # send some junk after the actual request
-        fd.write(b'01234567890123456789')
+        sock.sendall(b'01234567890123456789')
         result = read_http(sock)
         self.assertEqual(result.body, b'a is a, body is a=a')
-        fd.close()
 
     def test_008_correctresponse(self):
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('wb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         result_200 = read_http(sock)
-        fd.write(b'GET /notexist HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock.sendall(b'GET /notexist HTTP/1.1\r\nHost: localhost\r\n\r\n')
         read_http(sock)
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         result_test = read_http(sock)
         self.assertEqual(result_200.status, result_test.status)
-        fd.close()
-        sock.close()
 
     def test_009_chunked_response(self):
         self.site.application = chunked_app
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
-        assert b'Transfer-Encoding: chunked' in fd.read()
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
+        assert b'Transfer-Encoding: chunked' in recvall(sock)
 
     def test_010_no_chunked_http_1_0(self):
         self.site.application = chunked_app
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
-        assert b'Transfer-Encoding: chunked' not in fd.read()
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n')
+        assert b'Transfer-Encoding: chunked' not in recvall(sock)
 
     def test_011_multiple_chunks(self):
         self.site.application = big_chunks
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         fd = sock.makefile('rwb')
         fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
@@ -451,6 +388,54 @@ class TestHttpd(_TestBase):
         # Require a CRLF to close the message body
         self.assertEqual(response, b'\r\n')
 
+    def test_partial_writes_are_handled(self):
+        # https://github.com/eventlet/eventlet/issues/295
+        # Eventlet issue: "Python 3: wsgi doesn't handle correctly partial
+        # write of socket send() when using writelines()".
+        #
+        # 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.
+        #
+        # Switching wsgi wfile to buffered mode fixes the issue.
+        #
+        # 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):
+                b = b[: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(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
+        # This would previously hang forever
+        result = read_http(sock)
+        assert result.body == b'asd'
+
     @tests.skip_if_no_ssl
     def test_012_ssl_server(self):
         def wsgi_app(environ, start_response):
@@ -466,7 +451,7 @@ class TestHttpd(_TestBase):
                                         server_side=True)
         self.spawn_server(sock=server_sock, site=wsgi_app)
 
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock = eventlet.wrap_ssl(sock)
         sock.write(
             b'POST /foo HTTP/1.1\r\nHost: localhost\r\n'
@@ -496,7 +481,7 @@ class TestHttpd(_TestBase):
 
     def test_014_chunked_post(self):
         self.site.application = chunked_post
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write('PUT /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
                  'Transfer-Encoding: chunked\r\n\r\n'
@@ -508,7 +493,7 @@ class TestHttpd(_TestBase):
         response = fd.read()
         assert response == b'oh hai', 'invalid response %s' % response
 
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write('PUT /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
                  'Transfer-Encoding: chunked\r\n\r\n'
@@ -520,7 +505,7 @@ class TestHttpd(_TestBase):
         response = fd.read()
         assert response == b'oh hai', 'invalid response %s' % response
 
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write('PUT /c HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
                  'Transfer-Encoding: chunked\r\n\r\n'
@@ -534,17 +519,13 @@ class TestHttpd(_TestBase):
 
     def test_015_write(self):
         self.site.application = use_write
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('wb')
-        fd.write(b'GET /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
         result1 = read_http(sock)
         assert 'content-length' in result1.headers_lower
 
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('wb')
-        fd.write(b'GET /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
         result2 = read_http(sock)
         assert 'transfer-encoding' in result2.headers_lower
         assert result2.headers_lower['transfer-encoding'] == 'chunked'
@@ -557,7 +538,7 @@ class TestHttpd(_TestBase):
             start_response('200 OK', [('Content-Length', '7')])
             return [b'testing']
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write(b'GET /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
         fd.flush()
@@ -609,19 +590,15 @@ class TestHttpd(_TestBase):
     def test_018_http_10_keepalive(self):
         # verify that if an http/1.0 client sends connection: keep-alive
         # that we don't close the connection
-        sock = eventlet.connect(
-            ('localhost', self.port))
-
-        fd = sock.makefile('wb')
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
 
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
         result1 = read_http(sock)
         assert 'connection' in result1.headers_lower
         self.assertEqual('keep-alive', result1.headers_lower['connection'])
+
         # repeat request to verify connection is actually still open
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
-        fd.flush()
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
         result2 = read_http(sock)
         assert 'connection' in result2.headers_lower
         self.assertEqual('keep-alive', result2.headers_lower['connection'])
@@ -634,18 +611,15 @@ class TestHttpd(_TestBase):
             return [b'hello!']
 
         self.site.application = use_fieldstorage
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('rwb')
-        fd.write('POST / HTTP/1.1\r\n'
-                 'Host: localhost\r\n'
-                 'Connection: close\r\n'
-                 'Transfer-Encoding: chunked\r\n\r\n'
-                 '2\r\noh\r\n'
-                 '4\r\n hai\r\n0\r\n\r\n'.encode())
-        fd.flush()
-        assert b'hello!' in fd.read()
+        sock.sendall(b'POST / HTTP/1.1\r\n'
+                     b'Host: localhost\r\n'
+                     b'Connection: close\r\n'
+                     b'Transfer-Encoding: chunked\r\n\r\n'
+                     b'2\r\noh\r\n'
+                     b'4\r\n hai\r\n0\r\n\r\n')
+        assert b'hello!' in recvall(sock)
 
     def test_020_x_forwarded_for(self):
         request_bytes = (
@@ -653,7 +627,7 @@ class TestHttpd(_TestBase):
             + b'X-Forwarded-For: 1.2.3.4, 5.6.7.8\r\n\r\n'
         )
 
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(request_bytes)
         sock.recv(1024)
         sock.close()
@@ -663,7 +637,7 @@ class TestHttpd(_TestBase):
         self.logfile = six.StringIO()
         self.spawn_server(log_x_forwarded_for=False)
 
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(request_bytes)
         sock.recv(1024)
         sock.close()
@@ -677,12 +651,9 @@ class TestHttpd(_TestBase):
         server_sock_2 = server_sock.dup()
         self.spawn_server(sock=server_sock_2)
         # do a single req/response to verify it's up
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
-        fd.flush()
-        result = fd.read(1024)
-        fd.close()
+        sock = eventlet.connect(server_sock.getsockname())
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
+        result = sock.recv(1024)
         assert result.startswith(b'HTTP'), result
         assert result.endswith(b'hello world'), result
 
@@ -696,12 +667,9 @@ class TestHttpd(_TestBase):
         except socket.error as exc:
             self.assertEqual(support.get_errno(exc), errno.EBADF)
         self.spawn_server(sock=server_sock)
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
-        fd.flush()
-        result = fd.read(1024)
-        fd.close()
+        sock = eventlet.connect(server_sock.getsockname())
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
+        result = sock.recv(1024)
         assert result.startswith(b'HTTP'), result
         assert result.endswith(b'hello world'), result
 
@@ -718,14 +686,12 @@ class TestHttpd(_TestBase):
             start_response('200 OK', [('Content-type', 'text/plain')])
             return []
         self.site.application = clobberin_time
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write('GET / HTTP/1.1\r\n'
-                 'Host: localhost\r\n'
-                 'Connection: close\r\n'
-                 '\r\n\r\n'.encode())
-        fd.flush()
-        assert b'200 OK' in fd.read()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.1\r\n'
+                     b'Host: localhost\r\n'
+                     b'Connection: close\r\n'
+                     b'\r\n\r\n')
+        assert b'200 OK' in recvall(sock)
 
     def test_022_custom_pool(self):
         # just test that it accepts the parameter for now
@@ -735,24 +701,16 @@ class TestHttpd(_TestBase):
         self.spawn_server(custom_pool=p)
 
         # this stuff is copied from test_001_server, could be better factored
-        sock = eventlet.connect(
-            ('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
-        fd.flush()
-        result = fd.read()
-        fd.close()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
+        result = recvall(sock)
         assert result.startswith(b'HTTP'), result
         assert result.endswith(b'hello world'), result
 
     def test_023_bad_content_length(self):
-        sock = eventlet.connect(
-            ('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.0\r\nHost: localhost\r\nContent-length: argh\r\n\r\n')
-        fd.flush()
-        result = fd.read()
-        fd.close()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\nContent-length: argh\r\n\r\n')
+        result = recvall(sock)
         assert result.startswith(b'HTTP'), result
         assert b'400 Bad Request' in result, result
         assert b'500' not in result, result
@@ -767,7 +725,7 @@ class TestHttpd(_TestBase):
                 start_response('200 OK', [('Content-Length', str(len(text)))])
                 return [text]
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write(b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 1025\r\n'
                  b'Expect: 100-continue\r\n\r\n')
@@ -813,7 +771,7 @@ class TestHttpd(_TestBase):
                 start_response('200 OK', [('Content-Length', str(len(text)))])
                 return [text]
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write(b'PUT / HTTP/1.1\r\nHost: localhost\r\nContent-length: 1025\r\n'
                  b'Expect: 100-continue\r\n\r\n')
@@ -868,7 +826,7 @@ class TestHttpd(_TestBase):
             start_response('200 OK', [('Content-Length', str(len(text)))])
             return [text]
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write(b'PUT /a HTTP/1.1\r\n'
                  b'Host: localhost\r\nConnection: close\r\n'
@@ -946,7 +904,7 @@ class TestHttpd(_TestBase):
             return [text]
 
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fd = sock.makefile('rwb')
         fd.write(b'PUT /a HTTP/1.1\r\n'
                  b'Host: localhost\r\nConnection: close\r\n'
@@ -1015,7 +973,7 @@ class TestHttpd(_TestBase):
             self.spawn_server(sock=listener)
             eventlet.sleep(0)  # need to enter server loop
             try:
-                eventlet.connect(('localhost', self.port))
+                eventlet.connect(self.server_addr)
                 self.fail("Didn't expect to connect")
             except socket.error as exc:
                 self.assertEqual(support.get_errno(exc), errno.ECONNREFUSED)
@@ -1026,7 +984,7 @@ class TestHttpd(_TestBase):
 
     def test_026_log_format(self):
         self.spawn_server(log_format="HI %(request_line)s HI")
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(b'GET /yo! HTTP/1.1\r\nHost: localhost\r\n\r\n')
         sock.recv(1024)
         sock.close()
@@ -1037,7 +995,7 @@ class TestHttpd(_TestBase):
         # and we're not speaking with a 1.1 client, that we
         # close the connection
         self.site.application = chunked_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
 
@@ -1046,11 +1004,26 @@ class TestHttpd(_TestBase):
         self.assertNotEqual(result.headers_lower.get('transfer-encoding'), 'chunked')
         self.assertEqual(result.body, b"thisischunked")
 
+    def test_chunked_response_when_app_yields_empty_string(self):
+        def empty_string_chunked_app(env, start_response):
+            env['eventlet.minimum_write_chunk_size'] = 0  # no buffering
+            start_response('200 OK', [('Content-type', 'text/plain')])
+            return iter([b"stuff", b"", b"more stuff"])
+
+        self.site.application = empty_string_chunked_app
+        sock = eventlet.connect(self.server_addr)
+
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
+
+        result = read_http(sock)
+        self.assertEqual(result.headers_lower.get('transfer-encoding'), 'chunked')
+        self.assertEqual(result.body, b"5\r\nstuff\r\na\r\nmore stuff\r\n0\r\n\r\n")
+
     def test_minimum_chunk_size_parameter_leaves_httpprotocol_class_member_intact(self):
         start_size = wsgi.HttpProtocol.minimum_chunk_size
 
         self.spawn_server(minimum_chunk_size=start_size * 2)
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         read_http(sock)
 
@@ -1062,7 +1035,7 @@ class TestHttpd(_TestBase):
         self.spawn_server(minimum_chunk_size=1)
 
         self.site.application = chunked_fail_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
 
@@ -1081,8 +1054,7 @@ class TestHttpd(_TestBase):
         # verify that if an http/1.0 client sends connection: keep-alive
         # and the server doesn't accept keep-alives, we close the connection
         self.spawn_server(keepalive=False)
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(b'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n')
         result = read_http(sock)
@@ -1090,22 +1062,17 @@ class TestHttpd(_TestBase):
 
     def test_027_keepalive_chunked(self):
         self.site.application = chunked_post
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('wb')
+        sock = eventlet.connect(self.server_addr)
         common_suffix = (
             b'Host: localhost\r\nTransfer-Encoding: chunked\r\n\r\n' +
             b'10\r\n0123456789abcdef\r\n0\r\n\r\n')
-        fd.write(b'PUT /a HTTP/1.1\r\n' + common_suffix)
-        fd.flush()
+        sock.sendall(b'PUT /a HTTP/1.1\r\n' + common_suffix)
         read_http(sock)
-        fd.write(b'PUT /b HTTP/1.1\r\n' + common_suffix)
-        fd.flush()
+        sock.sendall(b'PUT /b HTTP/1.1\r\n' + common_suffix)
         read_http(sock)
-        fd.write(b'PUT /c HTTP/1.1\r\n' + common_suffix)
-        fd.flush()
+        sock.sendall(b'PUT /c HTTP/1.1\r\n' + common_suffix)
         read_http(sock)
-        fd.write(b'PUT /a HTTP/1.1\r\n' + common_suffix)
-        fd.flush()
+        sock.sendall(b'PUT /a HTTP/1.1\r\n' + common_suffix)
         read_http(sock)
         sock.close()
 
@@ -1127,9 +1094,9 @@ class TestHttpd(_TestBase):
                 eventlet.listen(('localhost', 0)),
                 certfile=certificate_file, keyfile=private_key_file,
                 server_side=True)
-            port = srv_sock.getsockname()[1]
+            addr = srv_sock.getsockname()
             g = eventlet.spawn_n(server, srv_sock)
-            client = eventlet.connect(('localhost', port))
+            client = eventlet.connect(addr)
             if data:  # send non-ssl request
                 client.sendall(data.encode())
             else:  # close sock prematurely
@@ -1138,7 +1105,7 @@ class TestHttpd(_TestBase):
             assert not errored[0], errored[0]
             # make another request to ensure the server's still alive
             try:
-                client = ssl.wrap_socket(eventlet.connect(('localhost', port)))
+                client = ssl.wrap_socket(eventlet.connect(addr))
                 client.write(b'GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
                 result = recvall(client)
                 assert result.startswith(b'HTTP'), result
@@ -1170,7 +1137,7 @@ class TestHttpd(_TestBase):
                 start_response('200 OK', [('Content-Type', 'text/plain')])
             yield b''
         self.site.application = one_posthook_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fp = sock.makefile('rwb')
         fp.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         fp.flush()
@@ -1193,7 +1160,7 @@ class TestHttpd(_TestBase):
                 start_response('200 OK', [('Content-Type', 'text/plain')])
             yield b''
         self.site.application = two_posthook_app
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         fp = sock.makefile('rwb')
         fp.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         fp.flush()
@@ -1204,7 +1171,7 @@ class TestHttpd(_TestBase):
         self.assertEqual(posthook2_count[0], 25)
 
     def test_030_reject_long_header_lines(self):
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         request = 'GET / HTTP/1.0\r\nHost: localhost\r\nLong: %s\r\n\r\n' % \
             ('a' * 10000)
         send_expect_close(sock, request.encode())
@@ -1212,7 +1179,7 @@ class TestHttpd(_TestBase):
         self.assertEqual(result.status, 'HTTP/1.0 400 Header Line Too Long')
 
     def test_031_reject_large_headers(self):
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         headers = ('Name: %s\r\n' % ('a' * 7000,)) * 20
         request = 'GET / HTTP/1.0\r\nHost: localhost\r\n%s\r\n\r\n' % headers
         send_expect_close(sock, request.encode())
@@ -1238,13 +1205,10 @@ class TestHttpd(_TestBase):
             'Host: localhost\r\n'
             'Content-Length: %i\r\n\r\n%s'
         ) % (len(upload_data), bytes_to_str(upload_data))
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(request.encode())
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(request.encode())
         result = read_http(sock)
         self.assertEqual(result.body, upload_data)
-        fd.close()
         self.assertEqual(g[0], 1)
 
     def test_zero_length_chunked_response(self):
@@ -1253,13 +1217,10 @@ class TestHttpd(_TestBase):
             yield b""
 
         self.site.application = zero_chunked_app
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
-        response = fd.read().split(b'\r\n')
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
+        response = recvall(sock).split(b'\r\n')
         headers = []
         while True:
             h = response.pop(0)
@@ -1273,8 +1234,7 @@ class TestHttpd(_TestBase):
 
     def test_configurable_url_length_limit(self):
         self.spawn_server(url_length_limit=20000)
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         path = 'x' * 15000
         request = 'GET /%s HTTP/1.0\r\nHost: localhost\r\n\r\n' % path
         fd = sock.makefile('rwb')
@@ -1301,6 +1261,7 @@ class TestHttpd(_TestBase):
             read_content.send(content)
             start_response('200 OK', [('Content-Type', 'text/plain')])
             return [content]
+
         self.site.application = chunk_reader
         expected_body = 'a bunch of stuff'
         data = "\r\n".join(['PUT /somefile HTTP/1.0',
@@ -1309,7 +1270,71 @@ class TestHttpd(_TestBase):
                             'def',
                             expected_body])
         # start PUT-ing some chunked data but close prematurely
-        sock = eventlet.connect(('127.0.0.1', self.port))
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(data.encode())
+        sock.close()
+        # the test passes if we successfully get here, and read all the data
+        # in spite of the early close
+        self.assertEqual(read_content.wait(), b'ok')
+        assert blew_up[0]
+
+    def test_aborted_chunked_post_between_chunks(self):
+        read_content = event.Event()
+        blew_up = [False]
+
+        def chunk_reader(env, start_response):
+            try:
+                content = env['wsgi.input'].read(1024)
+            except wsgi.ChunkReadError:
+                blew_up[0] = True
+                content = b'ok'
+            except Exception as err:
+                blew_up[0] = True
+                content = b'wrong exception: ' + str(err).encode()
+            read_content.send(content)
+            start_response('200 OK', [('Content-Type', 'text/plain')])
+            return [content]
+        self.site.application = chunk_reader
+        expected_body = 'A' * 0xdb
+        data = "\r\n".join(['PUT /somefile HTTP/1.0',
+                            'Transfer-Encoding: chunked',
+                            '',
+                            'db',
+                            expected_body])
+        # start PUT-ing some chunked data but close prematurely
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(data.encode())
+        sock.close()
+        # the test passes if we successfully get here, and read all the data
+        # in spite of the early close
+        self.assertEqual(read_content.wait(), b'ok')
+        assert blew_up[0]
+
+    def test_aborted_chunked_post_bad_chunks(self):
+        read_content = event.Event()
+        blew_up = [False]
+
+        def chunk_reader(env, start_response):
+            try:
+                content = env['wsgi.input'].read(1024)
+            except wsgi.ChunkReadError:
+                blew_up[0] = True
+                content = b'ok'
+            except Exception as err:
+                blew_up[0] = True
+                content = b'wrong exception: ' + str(err).encode()
+            read_content.send(content)
+            start_response('200 OK', [('Content-Type', 'text/plain')])
+            return [content]
+        self.site.application = chunk_reader
+        expected_body = 'look here is some data for you'
+        data = "\r\n".join(['PUT /somefile HTTP/1.0',
+                            'Transfer-Encoding: chunked',
+                            '',
+                            'cats',
+                            expected_body])
+        # start PUT-ing some garbage
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(data.encode())
         sock.close()
         # the test passes if we successfully get here, and read all the data
@@ -1321,10 +1346,8 @@ class TestHttpd(_TestBase):
         def wsgi_app(environ, start_response):
             raise RuntimeError("intentional error")
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         result = read_http(sock)
         self.assertEqual(result.status, 'HTTP/1.1 500 Internal Server Error')
         self.assertEqual(result.headers_lower['connection'], 'close')
@@ -1336,10 +1359,8 @@ class TestHttpd(_TestBase):
             yield b"oh hai, "
             yield u"xxx"
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
         result = read_http(sock)
         assert b'xxx' in result.body
 
@@ -1349,10 +1370,8 @@ class TestHttpd(_TestBase):
             yield b"oh hai, "
             yield u"xxx \u0230"
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
         result = read_http(sock)
         self.assertEqual(result.status, 'HTTP/1.1 500 Internal Server Error')
         self.assertEqual(result.headers_lower['connection'], 'close')
@@ -1363,10 +1382,8 @@ class TestHttpd(_TestBase):
             yield six.b("decoded: %s" % environ['PATH_INFO'])
             yield six.b("raw: %s" % environ['RAW_PATH_INFO'])
         self.site.application = wsgi_app
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('rwb')
-        fd.write(b'GET /a*b@%40%233 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET /a*b@%40%233 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
         result = read_http(sock)
         self.assertEqual(result.status, 'HTTP/1.1 200 OK')
         assert b'decoded: /a*b@@#3' in result.body
@@ -1402,10 +1419,8 @@ class TestHttpd(_TestBase):
             raise RuntimeError("intentional crash")
         self.site.application = crasher
 
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('wb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         result1 = read_http(sock)
         self.assertEqual(result1.status, 'HTTP/1.1 500 Internal Server Error')
         self.assertEqual(result1.body, b'')
@@ -1415,10 +1430,8 @@ class TestHttpd(_TestBase):
         # verify traceback when debugging enabled
         self.spawn_server(debug=True)
         self.site.application = crasher
-        sock = eventlet.connect(('localhost', self.port))
-        fd = sock.makefile('wb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-        fd.flush()
+        sock = eventlet.connect(self.server_addr)
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         result2 = read_http(sock)
         self.assertEqual(result2.status, 'HTTP/1.1 500 Internal Server Error')
         assert b'intentional crash' in result2.body, result2.body
@@ -1435,7 +1448,7 @@ class TestHttpd(_TestBase):
             yield b'a' * 9876
 
         server_sock = eventlet.listen(('localhost', 0))
-        self.port = server_sock.getsockname()[1]
+        self.server_addr = server_sock.getsockname()
         server = wsgi.Server(server_sock, server_sock.getsockname(), long_response,
                              log=self.logfile)
 
@@ -1459,7 +1472,7 @@ class TestHttpd(_TestBase):
 
     def test_server_socket_timeout(self):
         self.spawn_server(socket_timeout=0.1)
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.send(b'GET / HTTP/1.1\r\n')
         eventlet.sleep(0.1)
         try:
@@ -1480,7 +1493,7 @@ class TestHttpd(_TestBase):
 
         self.spawn_server(site=wsgi_app, capitalize_response_headers=False)
 
-        sock = eventlet.connect(('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         result = read_http(sock)
         sock.close()
@@ -1488,6 +1501,20 @@ class TestHttpd(_TestBase):
         self.assertEqual(result.headers_lower[random_case_header[0].lower()], random_case_header[1])
         self.assertEqual(result.headers_original[random_case_header[0]], random_case_header[1])
 
+    def test_log_unix_address(self):
+        tempdir = tempfile.mkdtemp('eventlet_test_log_unix_address')
+        path = ''
+        try:
+            sock = eventlet.listen(tempdir + '/socket', socket.AF_UNIX)
+            path = sock.getsockname()
+
+            log = six.StringIO()
+            self.spawn_server(sock=sock, log=log)
+            eventlet.sleep(0)  # need to enter server loop
+            assert 'http:' + path in log.getvalue()
+        finally:
+            shutil.rmtree(tempdir)
+
 
 def read_headers(sock):
     fd = sock.makefile('rb')
@@ -1527,18 +1554,14 @@ class IterableAlreadyHandledTest(_TestBase):
 
     def test_iterable_app_keeps_socket_open_unless_connection_close_sent(self):
         self.site.application = self.get_app()
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
-        fd = sock.makefile('rwb')
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
-
-        fd.flush()
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
         response_line, headers = read_headers(sock)
         self.assertEqual(response_line, 'HTTP/1.1 200 OK\r\n')
         assert 'connection' not in headers
-        fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
-        fd.flush()
+
+        sock.sendall(b'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
         result = read_http(sock)
         self.assertEqual(result.status, 'HTTP/1.1 200 OK')
         self.assertEqual(result.headers_lower.get('transfer-encoding'), 'chunked')
@@ -1558,7 +1581,6 @@ class ProxiedIterableAlreadyHandledTest(IterableAlreadyHandledTest):
 
 
 class TestChunkedInput(_TestBase):
-    dirt = ""
     validator = None
 
     def application(self, env, start_response):
@@ -1601,22 +1623,19 @@ class TestChunkedInput(_TestBase):
         return response
 
     def connect(self):
-        return eventlet.connect(('localhost', self.port))
+        return eventlet.connect(self.server_addr)
 
     def set_site(self):
         self.site = Site()
         self.site.application = self.application
 
-    def chunk_encode(self, chunks, dirt=None):
-        if dirt is None:
-            dirt = self.dirt
-
+    def chunk_encode(self, chunks, dirt=""):
         b = ""
         for c in chunks:
             b += "%x%s\r\n%s\r\n" % (len(c), dirt, c)
         return b
 
-    def body(self, dirt=None):
+    def body(self, dirt=""):
         return self.chunk_encode(["this", " is ", "chunked", "\nline",
                                   " 2", "\n", "line3", ""], dirt=dirt)