Add python-eventlet package to MOS 9.0 repository 91/17791/1 9.0 master mos-9.0
authorIvan Udovichenko <iudovichenko@mirantis.com>
Fri, 4 Mar 2016 16:30:28 +0000 (18:30 +0200)
committerIvan Udovichenko <iudovichenko@mirantis.com>
Fri, 4 Mar 2016 16:30:49 +0000 (18:30 +0200)
Change-Id: I34be69b0bce9526e64119f468b60cba2c5ec2aa1
Version: 0.18.4-1~u14.04+mos1
Source: http://http.debian.net/debian/pool/main/p/python-eventlet/python-eventlet_0.18.4-1.dsc

65 files changed:
debian/changelog
debian/control
debian/gbp.conf
debian/patches/greenio_send_was_running_empty_loop_on_ENOTCONN.patch [deleted file]
debian/patches/remove-self.assert-in-tests.patcher_test.py.patch
debian/patches/series
debian/patches/set-defaults-to-be-tlsv1-not-sslv23.patch
debian/patches/use-packaged-python-mock-rather-than-embedded.patch
debian/python-eventlet.lintian-overrides [deleted file]
debian/rules
debian/tests/control [new file with mode: 0644]
debian/tests/listen [new file with mode: 0755]
debian/tests/listen3 [new file with mode: 0755]
python-eventlet/AUTHORS
python-eventlet/NEWS
python-eventlet/bin/build-website.bash
python-eventlet/bin/release
python-eventlet/doc/Makefile
python-eventlet/doc/design_patterns.rst
python-eventlet/doc/real_index.html
python-eventlet/eventlet/__init__.py
python-eventlet/eventlet/backdoor.py
python-eventlet/eventlet/green/OpenSSL/__init__.py
python-eventlet/eventlet/green/select.py
python-eventlet/eventlet/green/selectors.py
python-eventlet/eventlet/green/ssl.py
python-eventlet/eventlet/green/subprocess.py
python-eventlet/eventlet/greenio/base.py
python-eventlet/eventlet/greenio/py2.py
python-eventlet/eventlet/greenio/py3.py
python-eventlet/eventlet/patcher.py
python-eventlet/eventlet/queue.py
python-eventlet/eventlet/tpool.py
python-eventlet/eventlet/websocket.py
python-eventlet/eventlet/wsgi.py
python-eventlet/tests/__init__.py
python-eventlet/tests/backdoor_test.py
python-eventlet/tests/db_pool_test.py
python-eventlet/tests/env_test.py
python-eventlet/tests/greenio_test.py
python-eventlet/tests/greenpipe_test_with_statement.py [deleted file]
python-eventlet/tests/isolated/env_tpool_negative.py [new file with mode: 0644]
python-eventlet/tests/isolated/env_tpool_size.py [new file with mode: 0644]
python-eventlet/tests/isolated/env_tpool_zero.py [new file with mode: 0644]
python-eventlet/tests/isolated/greendns_from_address_203.py
python-eventlet/tests/isolated/greenio_double_close_219.py
python-eventlet/tests/isolated/mysqldb_monkey_patch.py
python-eventlet/tests/isolated/patcher_blocking_select_methods_are_deleted.py [new file with mode: 0644]
python-eventlet/tests/isolated/patcher_importlib_lock.py
python-eventlet/tests/isolated/patcher_socketserver_selectors.py [new file with mode: 0644]
python-eventlet/tests/isolated/patcher_threading_condition.py
python-eventlet/tests/isolated/patcher_threading_join.py
python-eventlet/tests/isolated/subprocess_patched_communicate.py [new file with mode: 0644]
python-eventlet/tests/isolated/wsgi_connection_timeout.py
python-eventlet/tests/openssl_test.py [new file with mode: 0644]
python-eventlet/tests/patcher_test.py
python-eventlet/tests/semaphore_test.py
python-eventlet/tests/socket_test.py
python-eventlet/tests/subprocess_test.py
python-eventlet/tests/thread_test.py
python-eventlet/tests/tpool_test.py
python-eventlet/tests/websocket_new_test.py
python-eventlet/tests/websocket_test.py
python-eventlet/tests/wsgi_test.py
python-eventlet/tox.ini

index 2eee87a9f74bd108d069eb6255bfc3a68c681956..28547b1eb6d45f7fcd810dc8ed98956bb8dc632d 100644 (file)
@@ -1,3 +1,29 @@
+python-eventlet (0.18.4-1~u14.04+mos1) mos9.0; urgency=medium
+
+  * Source: http://http.debian.net/debian/pool/main/p/python-eventlet/python-eventlet_0.18.4-1.dsc
+
+ -- Ivan Udovichenko <iudovichenko@mirantis.com>  Fri, 04 Mar 2016 18:29:23 +0200
+
+python-eventlet (0.18.4-1) experimental; urgency=medium
+
+  [ Ondřej Nový ]
+  * Removed greenio_send_was_running_empty_loop_on_ENOTCONN.patch
+    (Applied upstream)
+  * Rebased use-packaged-python-mock-rather-than-embedded.patch
+  * Rebased set-defaults-to-be-tlsv1-not-sslv23.patch
+  * Rebased remove-self.assert-in-tests.patcher_test.py.patch
+  * Added python(3)-dnspython build dependency.
+  * Fixed VCS URLs (https).
+  * Standards-Version is 3.9.7 now (no change).
+  * Fixed upstream changelog.
+  * Added Debian tests.
+  * Added myself as uploader.
+
+  [ Corey Bryant ]
+  * New upstream release.
+
+ -- Ondřej Nový <novy@ondrej.org>  Fri, 26 Feb 2016 21:44:11 +0100
+
 python-eventlet (0.17.4-2~u14.04+mos1) mos8.0; urgency=medium
 
   * Source: http://http.debian.net/debian/pool/main/p/python-eventlet/python-eventlet_0.17.4-2.dsc
index 821eb1a4683c76749fa189350acc814047456b89..d6c80e775aa03c9c16f988276a2b35f0abe33d84 100644 (file)
@@ -2,8 +2,9 @@ Source: python-eventlet
 Section: python
 Priority: optional
 Maintainer: PKG OpenStack <openstack-devel@lists.alioth.debian.org>
-Uploaders: Laszlo Boszormenyi (GCS) <gcs@debian.hu>,
+Uploaders: Laszlo Boszormenyi (GCS) <gcs@debian.org>,
            Thomas Goirand <zigo@debian.org>,
+           Ondřej Nový <novy@ondrej.org>,
 Build-Depends: debhelper (>= 9),
                dh-python,
                openstack-pkg-tools,
@@ -12,21 +13,23 @@ Build-Depends: debhelper (>= 9),
                python-sphinx,
                python3-all,
                python3-setuptools,
-Build-Depends-Indep: python-greenlet,
+Build-Depends-Indep: python-dnspython,
+                     python-greenlet,
                      python-httplib2,
                      python-mock,
                      python-nose,
                      python-openssl,
                      python-zmq,
+                     python3-dnspython,
                      python3-greenlet,
                      python3-httplib2,
                      python3-mock,
                      python3-nose,
                      python3-openssl,
                      python3-zmq,
-Standards-Version: 3.9.6
-Vcs-Browser: http://anonscm.debian.org/gitweb/?p=openstack/python-eventlet.git;a=summary
-Vcs-Git: git://anonscm.debian.org/openstack/python-eventlet.git
+Standards-Version: 3.9.7
+Vcs-Browser: https://anonscm.debian.org/cgit/openstack/python-eventlet.git/
+Vcs-Git: https://anonscm.debian.org/git/openstack/python-eventlet.git
 Homepage: http://eventlet.net
 
 Package: python-eventlet
index 7bf5959802569ead90b3ae2183a97387c4e1782a..017bb3cfde02ef12919c1964f259e6ce182f5e05 100644 (file)
@@ -1,6 +1,6 @@
 [DEFAULT]
 upstream-branch = master
-debian-branch = debian/unstable
+debian-branch = debian/experimental
 upstream-tag = %(version)s
 compression = xz
 
diff --git a/debian/patches/greenio_send_was_running_empty_loop_on_ENOTCONN.patch b/debian/patches/greenio_send_was_running_empty_loop_on_ENOTCONN.patch
deleted file mode 100644 (file)
index 040c109..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-Description: greenio: send() was running empty loop on ENOTCONN
- Thanks to Seyeong Kim
- https://github.com/eventlet/eventlet/issues/192
-Author: Sergey Shepelev <temotor@gmail.com>
-Date: Fri, 15 May 2015 03:56:04 +0300
-
-diff --git a/AUTHORS b/AUTHORS
-index e0ab0e2..c57f010 100644
---- a/AUTHORS
-+++ b/AUTHORS
-@@ -119,3 +119,4 @@ Thanks To
- * Sean Dague, wsgi: Provide python logging compatibility
- * Tim Simmons, Use _socket_nodns and select in dnspython support
- * Antonio Cuni, fix fd double close on PyPy
-+* Seyeong Kim
-diff --git a/eventlet/greenio/base.py b/eventlet/greenio/base.py
-index 8da51ca..1e43176 100644
---- a/eventlet/greenio/base.py
-+++ b/eventlet/greenio/base.py
-@@ -358,7 +358,8 @@ def send(self, data, flags=0):
-             try:
-                 total_sent += fd.send(data[total_sent:], flags)
-             except socket.error as e:
--                if get_errno(e) not in SOCKET_BLOCKING:
-+                eno = get_errno(e)
-+                if eno == errno.ENOTCONN or eno not in SOCKET_BLOCKING:
-                     raise
-             if total_sent == len_data:
index 2946ca1441aa93ac1a17a3c90d11b4780ae29c61..2157ca5be95d7559bec09b4e500bbbad9227844e 100644 (file)
@@ -3,20 +3,18 @@ Author: Thomas Goirand <zigo@debian.org>
 Forwarded: no
 Last-Update: 2014-09-07
 
-Index: python-eventlet-0.17.3/tests/patcher_test.py
-===================================================================
---- python-eventlet-0.17.3.orig/tests/patcher_test.py
-+++ python-eventlet-0.17.3/tests/patcher_test.py
-@@ -325,7 +325,7 @@ print(len(_threading._active))
+--- a/tests/patcher_test.py
++++ b/tests/patcher_test.py
+@@ -327,7 +327,7 @@
          self.assertEqual(len(lines), 4, "\n".join(lines))
          assert lines[0].startswith('<Thread'), lines[0]
-         self.assertEqual(lines[1], "1", lines[1])
--        self.assertEqual(lines[2], "1", lines[2])
-+        #self.assertEqual(lines[2], "1", lines[2])
+         assert lines[1] == '1', lines
+-        assert lines[2] == '1', lines
++        #assert lines[2] == '1', lines
  
      def test_threading(self):
          new_mod = """import eventlet
-@@ -356,7 +356,7 @@ print(len(threading._active))
+@@ -358,7 +358,7 @@
  """
          self.write_to_tempfile("newmod", new_mod)
          output, lines = self.launch_subprocess('newmod')
index 92e3d2191d6f9520815557bee6d7c9d54f8ae2e9..e819f8256cc6636fc90a9781237c714d8ea889ef 100644 (file)
@@ -4,4 +4,3 @@ use-packaged-python-mock-rather-than-embedded.patch
 enforce-tlsv1-always.patch
 set-defaults-to-be-tlsv1-not-sslv23.patch
 fixed-privacy-breach-in-examples.patch
-greenio_send_was_running_empty_loop_on_ENOTCONN.patch
index 3df7176b677a1126182d5358e9f95f0dc4db06ba..e63fc680dab581f7dd2212cd1639f544cd4dc310 100644 (file)
@@ -4,9 +4,9 @@ Author: Thomas Goirand <zigo@debian.org>
 Forwarded: no
 Last-Update: 2015-05-21
 
---- python-eventlet-0.17.3.orig/eventlet/green/ssl.py
-+++ python-eventlet-0.17.3/eventlet/green/ssl.py
-@@ -46,7 +46,7 @@ class GreenSSLSocket(_original_sslsocket
+--- a/eventlet/green/ssl.py
++++ b/eventlet/green/ssl.py
+@@ -48,7 +48,7 @@
  
      def __init__(self, sock, keyfile=None, certfile=None,
                   server_side=False, cert_reqs=CERT_NONE,
index 41654e8d56fda0dfc4f7b1e9e6d0e6f988a58202..12a3bad092e967c08bfc128ce115018d81028c6f 100644 (file)
@@ -4,27 +4,26 @@ Author: Thomas Goirand <zigo@debian.org>
 Forwarded: no
 Last-Update: 2015-02-08
 
---- python-eventlet-0.16.1.orig/tests/db_pool_test.py
-+++ python-eventlet-0.16.1/tests/db_pool_test.py
-@@ -7,7 +7,8 @@ import os
+--- a/tests/db_pool_test.py
++++ b/tests/db_pool_test.py
+@@ -7,7 +7,8 @@
  import traceback
  from unittest import TestCase, main
  
--from tests import mock, skipped, skip_unless, skip_with_pyevent, get_database_auth
+-from tests import mock, skip_unless, skip_with_pyevent, get_database_auth
 +import mock
-+from tests import skipped, skip_unless, skip_with_pyevent, get_database_auth
++from tests import skip_unless, skip_with_pyevent, get_database_auth
  from eventlet import event
  from eventlet import db_pool
  from eventlet.support import six
---- python-eventlet-0.16.1.orig/tests/websocket_test.py
-+++ python-eventlet-0.16.1/tests/websocket_test.py
-@@ -8,7 +8,8 @@ from eventlet.green import httplib
- from eventlet.support import six
+--- a/tests/websocket_test.py
++++ b/tests/websocket_test.py
+@@ -9,7 +9,7 @@
  from eventlet.websocket import WebSocket, WebSocketWSGI
  
--from tests import certificate_file, LimitedTestCase, mock, private_key_file
+ import tests
+-from tests import mock
 +import mock
-+from tests import certificate_file, LimitedTestCase, private_key_file
- from tests import skip_if_no_ssl
- from tests.wsgi_test import _TestBase
+ import tests.wsgi_test
  
diff --git a/debian/python-eventlet.lintian-overrides b/debian/python-eventlet.lintian-overrides
deleted file mode 100644 (file)
index 9885900..0000000
+++ /dev/null
@@ -1 +0,0 @@
-python-eventlet: no-upstream-changelog
index 12e908db1be2f2da7360ff713e0e3565eba0db74..f0a1aef7e052087f4e8a31364000b7bf1dcb6d6b 100755 (executable)
@@ -37,3 +37,6 @@ override_dh_compress:
 override_dh_clean:
        dh_clean -O--buildsystem=python_distutils
        rm -rf build
+
+override_dh_installchangelogs:
+       dh_installchangelogs NEWS
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644 (file)
index 0000000..68bb763
--- /dev/null
@@ -0,0 +1,3 @@
+Tests: listen listen3
+Depends: python-eventlet,
+         python3-eventlet,
diff --git a/debian/tests/listen b/debian/tests/listen
new file mode 100755 (executable)
index 0000000..e6e1145
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python
+
+import eventlet
+
+eventlet.listen(('localhost', 7000))
diff --git a/debian/tests/listen3 b/debian/tests/listen3
new file mode 100755 (executable)
index 0000000..76179e0
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/python3
+
+import eventlet
+
+eventlet.listen(('localhost', 7000))
index e0ab0e2aa946225c1ef09fb66cc3a92748b061db..69e38c5a1d40c01f76178b1239edbabffd50898e 100644 (file)
@@ -39,6 +39,8 @@ Contributors
 * Victor Sergeyev
 * David Szotten
 * Victor Stinner
+* Samuel Merritt
+* Eric Urban
 
 Linden Lab Contributors
 -----------------------
@@ -91,7 +93,6 @@ Thanks To
 * Peter Skirko, fixing socket.settimeout(0) bug
 * Derk Tegeler, Pre-cache proxied GreenSocket methods (Bitbucket #136)
 * David Malcolm, optional "timeout" argument to the subprocess module (Bitbucket #89)
-* Eric Urban, fix wsgi.input 1-byte (Bitbucket #150)
 * David Goetz, wsgi: Allow minimum_chunk_size to be overriden on a per request basis
 * Dmitry Orlov, websocket: accept Upgrade: websocket (lowercase)
 * Zhang Hua, profile: accumulate results between runs (Bitbucket #162)
@@ -119,3 +120,13 @@ Thanks To
 * Sean Dague, wsgi: Provide python logging compatibility
 * Tim Simmons, Use _socket_nodns and select in dnspython support
 * Antonio Cuni, fix fd double close on PyPy
+* Seyeong Kim
+* Ihar Hrachyshka
+* Janusz Harkot
+* Fukuchi Daisuke
+* Ramakrishnan G
+* ashutosh-mishra
+* Azhar Hussain
+* Josh VanderLinden
+* Levente Polyak
+* Phus Lu
index 4e8df12bad1d475aafd070acf796231ee4e1160b..eb0ed9afc916d16558ed6c68efccd205810621c9 100644 (file)
@@ -1,3 +1,83 @@
+0.18.4
+======
+* wsgi: change TCP_NODELAY to TCP_QUICKACK, ignore socket error when not available
+
+0.18.3
+======
+* wsgi: Use buffered writes - fixes partial socket.send without custom
+  writelines(); Github issue #295
+* wsgi: TCP_NODELAY enabled by default
+
+0.18.2
+======
+* wsgi: Fix data loss on partial writes (socket.send); Thanks to Jakub Stasiak
+
+0.18.1
+======
+* IMPORTANT: do not use Eventlet 0.18.0 and 0.18.1
+* patcher: Fix AttributeError in subprocess communicate()
+* greenio: Fix "TypeError: an integer is required" in sendto()
+
+0.18.0
+======
+* IMPORTANT: do not use Eventlet 0.18.0 and 0.18.1
+* greenio: Fixed a bug that could cause send() to start an endless loop on
+  ENOTCONN; Thanks to Seyeong Kim
+* wsgi: Fixed UNIX socket address being trimmed in "wsgi starting" log; Thanks
+  to Ihar Hrachyshka
+* ssl: Ported eventlet.green.OpenSSL to Python 3; Thanks to Victor Stinner
+* greenio: Made read() support buflen=-1 and added readall() (Python 3);
+  Thanks to David Szotten
+* wsgi: Made the error raised in case of chunk read failures more precise (this
+  should be backwards compatible as the new exception class,
+  wsgi.ChunkReadError, is a subclass of ValueError which was being used there
+  before); Thanks to Samuel Merritt
+* greenio: Fixed socket.recv() sometimes returning str instead of bytes on
+  Python 3; Thanks to Janusz Harkot
+* wsgi: Improved request body discarding
+* websocket: Fixed TypeError on empty websocket message (Python 3); Thanks to
+  Fukuchi Daisuke
+* subprocess: Fixed universal_newlines support
+* wsgi: Output of 0-byte chunks is now suppressed; Thanks to Samuel Merritt
+* Improved the documentation; Thanks to Ramakrishnan G, ashutosh-mishra and
+  Azhar Hussain
+* greenio: Changed GreenFileIO.write() (Python 3) to always write all data to
+  match the behavior on Python 2; Thanks to Victor Stinner
+* subprocess: Fixed missing subprocess.mswindows attribute on Python 3.5;
+  Thanks to Josh VanderLinden
+* ssl/monkey patching: Fixed a bug that would cause merely importing eventlet
+  to monkey patch the ssl module; Thanks to David Szotten
+* documentation: Added support for building plain text documentation; thanks
+  to Levente Polyak
+* greenio: Fixed handling blocking IO errors in various GreenSocket methods;
+  Thanks to Victor Stinner
+* greenio: Fixed GreenPipe ignoring the bufsize parameter on Python 2; Thanks
+  to Phus Lu
+* backdoor: Added Unix and IPv6 socket support; Thanks to Eric Urban
+
+Backwards incompatible:
+
+* monkey patching: The following select methods and selector classes are now
+  removed, instead of being left in their respective modules after patching
+  even though they are not green (this also fixes HTTPServer.serve_forever()
+  blocking whole process on Python 3):
+
+  * select.poll
+  * select.epoll
+  * select.devpoll
+  * select.kqueue
+  * select.kevent
+  * selectors.PollSelector
+  * selectors.EpollSelector
+  * selectors.DevpollSelector
+  * selectors.KqueueSelector
+
+  Additionally selectors.DefaultSelector points to a green SelectSelector
+
+* greenio: Fixed send() to no longer behave like sendall() which makes it
+  consistent with Python standard library and removes a source of very subtle
+  errors
+
 0.17.4
 ======
 * ssl: incorrect initalization of default context; Thanks to stuart-mclaren
index 8f461e0db8902ee735e43ab3fdf89b2b30671c3b..6fd4e0154e2bb07d235a1f2d091cc6805e2b3c64 100755 (executable)
@@ -53,8 +53,7 @@ rm -f "doc/changelog.rst"
 
 if [ $commit -eq 1 ]; then
     echo "3. Updating git branch gh-pages"
-    source_name=`git rev-parse --abbrev-ref HEAD`
-    source_id=`git rev-parse --short HEAD`
+    source_name=`git describe --dirty --tags`
     git branch --track gh-pages origin/gh-pages || true
     git checkout gh-pages
     git ls-files |grep -Ev '^.gitignore$' |xargs rm -f
@@ -70,5 +69,5 @@ if [ $commit -eq 1 ]; then
     git status
 
     read -p "Carefully read git status output above, press Enter to continue or Ctrl+C to abort"
-    git commit --edit -m "Website built from $source_name $source_id"
+    git commit --edit -m "Website built from $source_name"
 fi
index 6480e7a5407cea7995fe6610fd2b289b3702a881..49d1082bce6f113ee03e04d55083d15ae411ed00 100755 (executable)
@@ -3,46 +3,158 @@ cd "$( dirname "${BASH_SOURCE[0]}" )/.."
 if [[ ! -d venv-release ]]; then
        virtualenv venv-release
        echo '*' >venv-release/.gitignore
-       venv-release/bin/pip install wheel sphinx
+       venv-release/bin/pip install -U pip setuptools sphinx wheel
 fi
 . $PWD/venv-release/bin/activate
 pip install -e $PWD
 
+version=
+version_next=
+
 main() {
        branch="${1-$(git symbolic-ref --short HEAD)}"
        version="$(python -c 'import eventlet; print(eventlet.__version__)')"
-       printf "branch: %s version: '%s'\n" $branch $version >&2
+       printf "\nbranch: %s eventlet.__version__: '%s'\n" $branch $version >&2
        if [[ "$branch" != "master" ]]; then
                echo "Must be on master" >&2
                exit 1
        fi
        if [[ -n "$(git status --short -uall)" ]]; then
-               echo "Tree must be clean" >&2
+               echo "Tree must be clean. git status:" >&2
+               echo "" >&2
+               git status --short -uall
+               echo "" >&2
                exit 1
        fi
+       last_commit_message=$(git show --format="%s" --no-patch HEAD)
+       expect_commit_message="v$version release"
+       if [[ "$last_commit_message" != "$expect_commit_message" ]]; then
+               printf "Last commit message: '%s' expected: '%s'\n" "$last_commit_message" "$expect_commit_message" >&2
+               if confirm "Create release commit? [yN] "; then
+                       create_commit
+               elif ! confirm "Continue without proper release commit? [yN] "; then
+                       exit 1
+               fi
+       fi
        confirm "Continue? [yN] " || exit 1
 
+       echo "Creating tag v$version" >&2
        if ! git tag "v$version"; then
-               echo "tag failed" >&2
+               echo "git tag failed " >&2
                confirm "Continue still? [yN] " || exit 1
        fi
 
+       if confirm "Build documentation (website)? [Yn] " >&2; then
+               bin/build-website.bash || exit 1
+       fi
+
        if confirm "Upload to PyPi? [Yn] "; then
                rm -rf build dist
-               python setup.py sdist bdist_wheel register upload
+               python setup.py sdist bdist_wheel register upload || exit 1
        fi
 
-       bin/build-website.bash
-
-       git push origin master
+       git push --verbose origin master gh-pages || exit 1
        git push --tags
-       git push origin gh-pages
+}
+
+create_commit() {
+       echo "" >&2
+       echo "Plan:" >&2
+       echo "1. bump version" >&2
+       echo "2. update NEWS, AUTHORS" >&2
+       echo "3. commit" >&2
+       echo "4. run bin/release again" >&2
+       echo "" >&2
+
+       bump_version
+       edit_news
+
+       git diff
+       confirm "Ready to commit? [Yn] " || exit 1
+       git commit -a -m "v$version_next release"
+
+       echo "Re-exec $0 to continue" >&2
+       exec $0
+}
+
+bump_version() {
+       local current=$version
+       echo "Current version: '$current'" >&2
+       echo -n "Enter next version (empty to abort): " >&2
+       read version_next
+       if [[ -z "$version_next" ]]; then
+               exit 1
+       fi
+       echo "Next version:    '$version_next'" >&2
+
+       local current_tuple="${current//./, }"
+       local next_tuple="${version_next//./, }"
+       local version_path="eventlet/__init__.py"
+       echo "Updating file '$version_path'" >&2
+       if ! sed -i '' -e "s/($current_tuple)/($next_tuple)/" "$version_path"; then
+               echo "sed error $?" >&2
+               exit 1
+       fi
+       if git diff --exit-code "$version_path"; then
+               echo "File '$version_path' is not modified" >&2
+               exit 1
+       fi
+       echo "" >&2
+
+       local doc_path="doc/real_index.html"
+       echo "Updating file '$doc_path'" >&2
+       if ! sed -i '' -e "s/$current/$version_next/g" "$doc_path"; then
+               echo "sed error $?" >&2
+               exit 1
+       fi
+       if git diff --exit-code "$doc_path"; then
+               echo "File '$doc_path' is not modified" >&2
+               exit 1
+       fi
+       echo "" >&2
+
+       confirm "Confirm changes? [yN] " || exit 1
+}
+
+edit_news() {
+       echo "Changes since last release:" >&2
+       git log --format='%h   %an   %s' "v$version"^.. -- || exit 1
+       echo "" >&2
+
+       local editor=$(which edit 2>/dev/null)
+       [[ -z "$editor" ]] && editor="$EDITOR"
+       if [[ -n "$editor" ]]; then
+               if confirm "Open default editor for NEWS and AUTHORS? [Yn] "; then
+                       $editor NEWS
+                       $editor AUTHORS
+               else
+                       confirm "Change files NEWS and AUTHORS manually and press any key"
+               fi
+       else
+               echo "Unable to determine default text editor." >&2
+               confirm "Change files NEWS and AUTHORS manually and press any key"
+       fi
+       echo "" >&2
+
+       if git diff --exit-code NEWS AUTHORS; then
+               echo "Files NEWS and AUTHORS are not modified" >&2
+               exit 1
+       fi
+       echo "" >&2
+
+       confirm "Confirm changes? [yN] " || exit 1
 }
 
 confirm() {
-       read -n1 -p "$1" reply
-       echo ""
+       local reply
+       local prompt="$1"
+       read -n1 -p "$prompt" reply >&2
+       echo "" >&2
        rc=0
+       local default_y=" \[Yn\] $"
+       if [[ -z "$reply" ]] && [[ "$prompt" =~ $default_y ]]; then
+               reply="y"
+       fi
        [[ "$reply" != "y" ]] && rc=1
        return $rc
 }
index 076db3ab114b164aaab6d2547b1dcf07aa5cfa01..64f01344ea913b2741b36140df1f42f4667a5b32 100644 (file)
@@ -11,10 +11,11 @@ PAPEROPT_a4     = -D latex_paper_size=a4
 PAPEROPT_letter = -D latex_paper_size=letter
 ALLSPHINXOPTS   = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 
-.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+.PHONY: help clean text html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
 
 help:
        @echo "Please use \`make <target>' where <target> is one of"
+       @echo "  text      to make text files"
        @echo "  html      to make standalone HTML files"
        @echo "  dirhtml   to make HTML files named index.html in directories"
        @echo "  pickle    to make pickle files"
@@ -30,6 +31,11 @@ help:
 clean:
        -rm -rf _build/*
 
+text:
+       $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) _build/text
+       @echo
+       @echo "Build finished. The text files are in _build/text."
+
 html:
        $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
        @echo
index f27f37d85465f4d5498d7b396eb18756eb849869..0f844090e98a7d3115dc0c82b72dca89099a46a2 100644 (file)
@@ -14,7 +14,7 @@ The canonical client-side example is a web crawler.  This use case is given a li
     from eventlet.green import urllib2
 
     urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
-           "https://wiki.secondlife.com/w/images/secondlife.jpg",
+           "https://www.python.org/static/img/python-logo.png",
            "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif"]
 
     def fetch(url):
index d9a7d600d9c00728bd99f16dc904f5699ac4a234..22a0ba011a671f7e6531ab686e76c3575c21d647 100644 (file)
@@ -1,8 +1,8 @@
 <!doctype html>
 
-<html>
+<html lang="en">
   <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="UTF-8" />
 
     <title>Eventlet Networking Library</title>
     <link rel="stylesheet" href="doc/_static/default.css" type="text/css" />
 
 <h3>Installation</h3>
 
-<p>To install eventlet, simply:
+<p>To install Eventlet, simply:
 <pre>
 pip install eventlet
 </pre></p>
 
-<p>Alternately, you can download the source tarball:</p>
+<p>Alternately, you can download the source archive:</p>
 <ul>
 <li>latest release from <a class="reference external" target="_blank" href="https://pypi.python.org/pypi/eventlet/">PyPi</a>:
-  <a class="reference external" href="https://pypi.python.org/packages/source/e/eventlet/eventlet-0.17.4.tar.gz">eventlet-0.17.4.tar.gz</a></li>
+  <a class="reference external" href="https://pypi.python.org/packages/source/e/eventlet/eventlet-0.18.4.tar.gz">eventlet-0.18.4.tar.gz</a></li>
 <li>or <a class="reference external" href="https://github.com/eventlet/eventlet/archive/master.zip">latest development version</a></li>
 </ul>
 
index 1bdcf52829a44fa0e8cbddea82c97cb2c7bce718..444e37fb75f961d15c42f2638670530a0442e664 100644 (file)
@@ -1,4 +1,4 @@
-version_info = (0, 17, 4)
+version_info = (0, 18, 4)
 __version__ = '.'.join(map(str, version_info))
 
 try:
index 2067772427bdbe62a639e71b096ef6a9f9f89646..9a6797a3ae0dc22abf5578db0c74ad68240e167d 100644 (file)
@@ -4,6 +4,8 @@ from code import InteractiveConsole
 import errno
 import socket
 import sys
+import errno
+import traceback
 
 import eventlet
 from eventlet import hubs
@@ -69,7 +71,12 @@ class SocketConsole(greenlets.greenlet):
     def finalize(self):
         # restore the state of the socket
         self.desc = None
-        print("backdoor closed to %s:%s" % self.hostport)
+        if len(self.hostport) >= 2:
+            host = self.hostport[0]
+            port = self.hostport[1]
+            print("backdoor closed to %s:%s" % (host, port,))
+        else:
+            print('backdoor closed')
 
 
 def backdoor_server(sock, locals=None):
@@ -81,7 +88,16 @@ def backdoor_server(sock, locals=None):
     of the interpreters.  It can be convenient to stick important application
     variables in here.
     """
-    print("backdoor server listening on %s:%s" % sock.getsockname())
+    listening_on = sock.getsockname()
+    if sock.family == socket.AF_INET:
+        # Expand result to IP + port
+        listening_on = '%s:%s' % listening_on
+    elif sock.family == socket.AF_INET6:
+        ip, port, _, _ = listening_on
+        listening_on = '%s:%s' % (ip, port,)
+    # No action needed if sock.family == socket.AF_UNIX
+
+    print("backdoor server listening on %s" % (listening_on,))
     try:
         try:
             while True:
@@ -102,10 +118,16 @@ def backdoor(conn_info, locals=None):
     (such as backdoor_server).
     """
     conn, addr = conn_info
-    host, port = addr
-    print("backdoor to %s:%s" % (host, port))
+    if conn.family == socket.AF_INET:
+        host, port = addr
+        print("backdoor to %s:%s" % (host, port))
+    elif conn.family == socket.AF_INET6:
+        host, port, _, _ = addr
+        print("backdoor to %s:%s" % (host, port))
+    else:
+        print('backdoor opened')
     fl = conn.makefile("rw")
-    console = SocketConsole(fl, (host, port), locals)
+    console = SocketConsole(fl, addr, locals)
     hub = hubs.get_hub()
     hub.schedule_call_global(0, console.switch)
 
index 56bfb8adfd44a1ff91558143c6991fbd33e2bbcc..26b60d944c66f0880bd1583270599530ba1032bf 100644 (file)
@@ -1,5 +1,5 @@
-import rand
-import crypto
-import SSL
-import tsafe
-from version import __version__
+from . import rand
+from . import crypto
+from . import SSL
+from . import tsafe
+from .version import __version__
index 53fb3598efe197da851ff185329be310342c089a..d1cba125fd84f5a3559e453958b46b5623860a96 100644 (file)
@@ -6,6 +6,9 @@ from eventlet.support import six
 
 
 __patched__ = ['select']
+# FIXME: must also delete `poll`, but it breaks subprocess `communicate()`
+# https://github.com/eventlet/eventlet/issues/290
+__deleted__ = ['devpoll', 'epoll', 'kqueue', 'kevent']
 
 
 def get_fileno(obj):
index 26427ec187108efec9aa9725c69d8bf9d189a325..81fc8628cfba38883772435956a39acca69c1b5f 100644 (file)
@@ -3,9 +3,32 @@ import sys
 from eventlet import patcher
 from eventlet.green import select
 
+__patched__ = [
+    'DefaultSelector',
+    'SelectSelector',
+]
+
+# We only have green select so the options are:
+# * leave it be and have selectors that block
+# * try to pretend the "bad" selectors don't exist
+# * replace all with SelectSelector for the price of possibly different
+#   performance characteristic and missing fileno() method (if someone
+#   uses it it'll result in a crash, we may want to implement it in the future)
+#
+# This module used to follow the third approach but just removing the offending
+# selectors is less error prone and less confusing approach.
+__deleted__ = [
+    'PollSelector',
+    'EpollSelector',
+    'DevpollSelector',
+    'KqueueSelector',
+]
+
 patcher.inject('selectors', globals(), ('select', select))
 
 del patcher
 
 if sys.platform != 'win32':
     SelectSelector._select = staticmethod(select.select)
+
+DefaultSelector = SelectSelector
index ded65338c8d59bad2c7d26b7be9f21b3c81d979d..81eae3dc43a7958d0a1924715e232fdb0ebee633 100644 (file)
@@ -22,7 +22,9 @@ else:
     has_ciphers = False
     timeout_exc = orig_socket.timeout
 
-__patched__ = ['SSLSocket', 'wrap_socket', 'sslwrap_simple']
+__patched__ = [
+    'SSLSocket', 'SSLContext', 'wrap_socket', 'sslwrap_simple',
+    'create_default_context', '_create_default_https_context']
 
 _original_sslsocket = __ssl.SSLSocket
 
@@ -357,11 +359,27 @@ if hasattr(__ssl, 'sslwrap_simple'):
 
 
 if hasattr(__ssl, 'SSLContext'):
-    @functools.wraps(__ssl.SSLContext.wrap_socket)
-    def _green_sslcontext_wrap_socket(self, sock, *a, **kw):
-        return GreenSSLSocket(sock, *a, _context=self, **kw)
-
-    # FIXME:
-    # * GreenSSLContext akin to GreenSSLSocket
-    # * make ssl.create_default_context() use modified SSLContext from globals as usual
-    __ssl.SSLContext.wrap_socket = _green_sslcontext_wrap_socket
+    _original_sslcontext = __ssl.SSLContext
+
+    class GreenSSLContext(_original_sslcontext):
+        __slots__ = ()
+
+        def wrap_socket(self, sock, *a, **kw):
+            return GreenSSLSocket(sock, *a, _context=self, **kw)
+
+    SSLContext = GreenSSLContext
+
+    if hasattr(__ssl, 'create_default_context'):
+        _original_create_default_context = __ssl.create_default_context
+
+        def green_create_default_context(*a, **kw):
+            # We can't just monkey-patch on the green version of `wrap_socket`
+            # on to SSLContext instances, but SSLContext.create_default_context
+            # does a bunch of work. Rather than re-implementing it all, just
+            # switch out the __class__ to get our `wrap_socket` implementation
+            context = _original_create_default_context(*a, **kw)
+            context.__class__ = GreenSSLContext
+            return context
+
+        create_default_context = green_create_default_context
+        _create_default_https_context = green_create_default_context
index 7ce38cfb11d1790faf1110992df8218a1f9cc8ce..13474043f3377a99c99b20455d7329b3f5e23013 100644 (file)
@@ -17,6 +17,7 @@ if sys.version_info > (3, 4):
 
 patcher.inject('subprocess', globals(), *to_patch)
 subprocess_orig = __import__("subprocess")
+mswindows = sys.platform == "win32"
 
 
 if getattr(subprocess_orig, 'TimeoutExpired', None) is None:
@@ -46,7 +47,7 @@ class Popen(subprocess_orig.Popen):
     # Windows. (see eventlet.greenio.set_nonblocking()) As the sole purpose of
     # this __init__() override is to wrap the pipes for eventlet-friendly
     # non-blocking I/O, don't even bother overriding it on Windows.
-    if not subprocess_orig.mswindows:
+    if not mswindows:
         def __init__(self, args, bufsize=0, *argss, **kwds):
             self.args = args
             # Forward the call to base-class constructor
@@ -55,8 +56,19 @@ class Popen(subprocess_orig.Popen):
             # eventlet.processes.Process.run() method.
             for attr in "stdin", "stdout", "stderr":
                 pipe = getattr(self, attr)
-                if pipe is not None and not type(pipe) == greenio.GreenPipe:
-                    wrapped_pipe = greenio.GreenPipe(pipe, pipe.mode, bufsize)
+                if pipe is not None and type(pipe) != greenio.GreenPipe:
+                    # https://github.com/eventlet/eventlet/issues/243
+                    # AttributeError: '_io.TextIOWrapper' object has no attribute 'mode'
+                    mode = getattr(pipe, 'mode', '')
+                    if not mode:
+                        if pipe.readable():
+                            mode += 'r'
+                        if pipe.writable():
+                            mode += 'w'
+                        # ValueError: can't have unbuffered text I/O
+                        if bufsize == 0:
+                            bufsize = -1
+                    wrapped_pipe = greenio.GreenPipe(pipe, mode, bufsize)
                     setattr(self, attr, wrapped_pipe)
         __init__.__doc__ = subprocess_orig.Popen.__init__.__doc__
 
@@ -82,7 +94,7 @@ class Popen(subprocess_orig.Popen):
                 raise
     wait.__doc__ = subprocess_orig.Popen.wait.__doc__
 
-    if not subprocess_orig.mswindows:
+    if not mswindows:
         # don't want to rewrite the original _communicate() method, we
         # just want a version that uses eventlet.green.select.select()
         # instead of select.select().
index 8da51caa88f155062297e0c7290cd193dd27df9f..e1c4a4552d46aae884ed05e13c59dd2d08737c5d 100644 (file)
@@ -1,13 +1,13 @@
 import errno
 import os
-from socket import socket as _original_socket
 import socket
 import sys
 import time
 import warnings
 
-from eventlet.support import get_errno, six
+import eventlet
 from eventlet.hubs import trampoline, notify_opened, IOClosed
+from eventlet.support import get_errno, six
 
 __all__ = [
     'GreenSocket', '_GLOBAL_DEFAULT_TIMEOUT', 'set_nonblocking',
@@ -24,6 +24,8 @@ if sys.platform[:3] == "win":
 if six.PY2:
     _python2_fileobject = socket._fileobject
 
+_original_socket = eventlet.patcher.original('socket').socket
+
 
 def socket_connect(descriptor, address):
     """
@@ -304,73 +306,80 @@ class GreenSocket(object):
                       "makefile instead", DeprecationWarning, stacklevel=2)
         return self.makefile(*args, **kw)
 
-    def recv(self, buflen, flags=0):
+    def _read_trampoline(self):
+        self._trampoline(
+            self.fd,
+            read=True,
+            timeout=self.gettimeout(),
+            timeout_exc=socket.timeout("timed out"))
+
+    def _recv_loop(self, recv_meth, *args):
         fd = self.fd
         if self.act_non_blocking:
-            return fd.recv(buflen, flags)
+            return recv_meth(*args)
+
         while True:
             try:
-                return fd.recv(buflen, flags)
+                # recv: bufsize=0?
+                # recv_into: buffer is empty?
+                # This is needed because behind the scenes we use sockets in
+                # nonblocking mode and builtin recv* methods. Attempting to read
+                # 0 bytes from a nonblocking socket using a builtin recv* method
+                # does not raise a timeout exception. Since we're simulating
+                # a blocking socket here we need to produce a timeout exception
+                # if needed, hence the call to trampoline.
+                if not args[0]:
+                    self._read_trampoline()
+                return recv_meth(*args)
             except socket.error as e:
                 if get_errno(e) in SOCKET_BLOCKING:
                     pass
                 elif get_errno(e) in SOCKET_CLOSED:
-                    return ''
+                    return b''
                 else:
                     raise
+
             try:
-                self._trampoline(
-                    fd,
-                    read=True,
-                    timeout=self.gettimeout(),
-                    timeout_exc=socket.timeout("timed out"))
+                self._read_trampoline()
             except IOClosed as e:
                 # Perhaps we should return '' instead?
                 raise EOFError()
 
-    def recvfrom(self, *args):
-        if not self.act_non_blocking:
-            self._trampoline(self.fd, read=True, timeout=self.gettimeout(),
-                             timeout_exc=socket.timeout("timed out"))
-        return self.fd.recvfrom(*args)
+    def recv(self, bufsize, flags=0):
+        return self._recv_loop(self.fd.recv, bufsize, flags)
 
-    def recvfrom_into(self, *args):
-        if not self.act_non_blocking:
-            self._trampoline(self.fd, read=True, timeout=self.gettimeout(),
-                             timeout_exc=socket.timeout("timed out"))
-        return self.fd.recvfrom_into(*args)
+    def recvfrom(self, bufsize, flags=0):
+        return self._recv_loop(self.fd.recvfrom, bufsize, flags)
 
-    def recv_into(self, *args):
-        if not self.act_non_blocking:
-            self._trampoline(self.fd, read=True, timeout=self.gettimeout(),
-                             timeout_exc=socket.timeout("timed out"))
-        return self.fd.recv_into(*args)
+    def recv_into(self, buffer, nbytes=0, flags=0):
+        return self._recv_loop(self.fd.recv_into, buffer, nbytes, flags)
 
-    def send(self, data, flags=0):
-        fd = self.fd
+    def recvfrom_into(self, buffer, nbytes=0, flags=0):
+        return self._recv_loop(self.fd.recvfrom_into, buffer, nbytes, flags)
+
+    def _send_loop(self, send_method, data, *args):
         if self.act_non_blocking:
-            return fd.send(data, flags)
+            return send_method(data, *args)
 
-        # blocking socket behavior - sends all, blocks if the buffer is full
-        total_sent = 0
-        len_data = len(data)
         while 1:
             try:
-                total_sent += fd.send(data[total_sent:], flags)
+                return send_method(data, *args)
             except socket.error as e:
-                if get_errno(e) not in SOCKET_BLOCKING:
+                eno = get_errno(e)
+                if eno == errno.ENOTCONN or eno not in SOCKET_BLOCKING:
                     raise
 
-            if total_sent == len_data:
-                break
-
             try:
                 self._trampoline(self.fd, write=True, timeout=self.gettimeout(),
                                  timeout_exc=socket.timeout("timed out"))
             except IOClosed:
                 raise socket.error(errno.ECONNRESET, 'Connection closed by another thread')
 
-        return total_sent
+    def send(self, data, flags=0):
+        return self._send_loop(self.fd.send, data, flags)
+
+    def sendto(self, data, *args):
+        return self._send_loop(self.fd.sendto, data, *args)
 
     def sendall(self, data, flags=0):
         tail = self.send(data, flags)
@@ -378,10 +387,6 @@ class GreenSocket(object):
         while tail < len_data:
             tail += self.send(data[tail:], flags)
 
-    def sendto(self, *args):
-        self._trampoline(self.fd, write=True)
-        return self.fd.sendto(*args)
-
     def setblocking(self, flag):
         if flag:
             self.act_non_blocking = False
index a0e9efebd0bc1b89eb6dac052fa2a04429d15158..471391b884214fa199c65a0483d385acb4eb8dd2 100644 (file)
@@ -38,7 +38,7 @@ class GreenPipe(_fileobject):
             self._name = f.name
             f.close()
 
-        super(GreenPipe, self).__init__(_SocketDuckForFd(fileno), mode)
+        super(GreenPipe, self).__init__(_SocketDuckForFd(fileno), mode, bufsize)
         set_nonblocking(self)
         self.softspace = 0
 
index 338ac687ee11cd12b32240e4ed5752a5014c4bbd..f2248e15ee783fa46abd5c6f433afbfb4b8d06c8 100644 (file)
@@ -75,10 +75,26 @@ class GreenFileIO(_OriginalIOBase):
     def fileno(self):
         return self._fileno
 
-    def read(self, buflen):
+    def read(self, size=-1):
+        if size == -1:
+            return self.readall()
+
+        while True:
+            try:
+                return _original_os.read(self._fileno, size)
+            except OSError as e:
+                if get_errno(e) not in SOCKET_BLOCKING:
+                    raise IOError(*e.args)
+            self._trampoline(self, read=True)
+
+    def readall(self):
+        buf = []
         while True:
             try:
-                return _original_os.read(self._fileno, buflen)
+                chunk = _original_os.read(self._fileno, DEFAULT_BUFFER_SIZE)
+                if chunk == b'':
+                    return b''.join(buf)
+                buf.append(chunk)
             except OSError as e:
                 if get_errno(e) not in SOCKET_BLOCKING:
                     raise IOError(*e.args)
@@ -116,14 +132,19 @@ class GreenFileIO(_OriginalIOBase):
         self._closed = True
 
     def write(self, data):
-        while True:
+        view = memoryview(data)
+        datalen = len(data)
+        offset = 0
+        while offset < datalen:
             try:
-                return _original_os.write(self._fileno, data)
+                written = _original_os.write(self._fileno, view[offset:])
             except OSError as e:
                 if get_errno(e) not in SOCKET_BLOCKING:
                     raise IOError(*e.args)
-                else:
-                    trampoline(self, write=True)
+                trampoline(self, write=True)
+            else:
+                offset += written
+        return offset
 
     def close(self):
         if not self._closed:
index eb09f9ad6c44509c8fdc8c423d9030ffa92d10b4..3a9804e19dc178314e4f33d51aa34289465ff6b7 100644 (file)
@@ -251,27 +251,19 @@ def monkey_patch(**on):
         on.setdefault(modname, default_on)
 
     modules_to_patch = []
-    if on['os'] and not already_patched.get('os'):
-        modules_to_patch += _green_os_modules()
-        already_patched['os'] = True
-    if on['select'] and not already_patched.get('select'):
-        modules_to_patch += _green_select_modules()
-        already_patched['select'] = True
-    if on['socket'] and not already_patched.get('socket'):
-        modules_to_patch += _green_socket_modules()
-        already_patched['socket'] = True
-    if on['thread'] and not already_patched.get('thread'):
-        modules_to_patch += _green_thread_modules()
-        already_patched['thread'] = True
-    if on['time'] and not already_patched.get('time'):
-        modules_to_patch += _green_time_modules()
-        already_patched['time'] = True
-    if on.get('MySQLdb') and not already_patched.get('MySQLdb'):
-        modules_to_patch += _green_MySQLdb()
-        already_patched['MySQLdb'] = True
-    if on.get('builtins') and not already_patched.get('builtins'):
-        modules_to_patch += _green_builtins()
-        already_patched['builtins'] = True
+    for name, modules_function in [
+        ('os', _green_os_modules),
+        ('select', _green_select_modules),
+        ('socket', _green_socket_modules),
+        ('thread', _green_thread_modules),
+        ('time', _green_time_modules),
+        ('MySQLdb', _green_MySQLdb),
+        ('builtins', _green_builtins),
+    ]:
+        if on[name] and not already_patched.get(name):
+            modules_to_patch += modules_function()
+            already_patched[name] = True
+
     if on['psycopg'] and not already_patched.get('psycopg'):
         try:
             from eventlet.support import psycopg2_patcher
@@ -295,6 +287,10 @@ def monkey_patch(**on):
                 patched_attr = getattr(mod, attr_name, None)
                 if patched_attr is not None:
                     setattr(orig_mod, attr_name, patched_attr)
+            deleted = getattr(mod, '__deleted__', [])
+            for attr_name in deleted:
+                if hasattr(orig_mod, attr_name):
+                    delattr(orig_mod, attr_name)
     finally:
         imp.release_lock()
 
@@ -331,7 +327,13 @@ def _green_os_modules():
 
 def _green_select_modules():
     from eventlet.green import select
-    return [('select', select)]
+    modules = [('select', select)]
+
+    if sys.version_info >= (3, 4):
+        from eventlet.green import selectors
+        modules.append(('selectors', selectors))
+
+    return modules
 
 
 def _green_socket_modules():
index 5a82238193670c5f9b2384b7a9075cdaf72c0ef9..31248db70552b03c8454c809bcdae3f8ac86429d 100644 (file)
@@ -50,6 +50,7 @@ from eventlet.event import Event
 from eventlet.greenthread import getcurrent
 from eventlet.hubs import get_hub
 from eventlet.support import six
+from eventlet.support.six.moves import queue as Stdlib_Queue
 from eventlet.timeout import Timeout
 
 
@@ -145,9 +146,10 @@ class Waiter(object):
 class LightQueue(object):
     """
     This is a variant of Queue that behaves mostly like the standard
-    :class:`Queue`.  It differs by not supporting the
-    :meth:`task_done <Queue.task_done>` or :meth:`join <Queue.join>` methods,
-    and is a little faster for not having that overhead.
+    :class:`Stdlib_Queue`.  It differs by not supporting the
+    :meth:`task_done <Stdlib_Queue.task_done>` or
+    :meth:`join <Stdlib_Queue.join>` methods, and is a little faster for
+    not having that overhead.
     """
 
     def __init__(self, maxsize=None):
@@ -381,11 +383,11 @@ class Queue(LightQueue):
     If *maxsize* is less than zero or ``None``, the queue size is infinite.
 
     ``Queue(0)`` is a channel, that is, its :meth:`put` method always blocks
-    until the item is delivered. (This is unlike the standard :class:`Queue`,
-    where 0 means infinite size).
+    until the item is delivered. (This is unlike the standard
+    :class:`Stdlib_Queue`, where 0 means infinite size).
 
-    In all other respects, this Queue class resembled the standard library,
-    :class:`Queue`.
+    In all other respects, this Queue class resembles the standard library,
+    :class:`Stdlib_Queue`.
     '''
 
     def __init__(self, maxsize=None):
index 8d73814d437b6586c9eee218e793f2e7d724b99e..618c37799136acc23e7a854910f9e03e29fa4755 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import atexit
 import imp
 import os
 import sys
 import traceback
 
+import eventlet
 from eventlet import event, greenio, greenthread, patcher, timeout
 from eventlet.support import six
 
@@ -39,7 +41,7 @@ if six.PY3:
 Empty = Queue_module.Empty
 Queue = Queue_module.Queue
 
-_bytetosend = ' '.encode()
+_bytetosend = b' '
 _coro = None
 _nthreads = int(os.environ.get('EVENTLET_THREADPOOL_SIZE', 20))
 _reqq = _rspq = None
@@ -54,6 +56,7 @@ def tpool_trampoline():
         try:
             _c = _rsock.recv(1)
             assert _c
+        # FIXME: this is probably redundant since using sockets instead of pipe now
         except ValueError:
             break  # will be raised when pipe is closed
         while not _rspq.empty():
@@ -250,29 +253,33 @@ class Proxy(object):
 
 
 def setup():
-    global _rsock, _wsock, _threads, _coro, _setup_already, _rspq, _reqq
+    global _rsock, _wsock, _coro, _setup_already, _rspq, _reqq
     if _setup_already:
         return
     else:
         _setup_already = True
 
+    assert _nthreads >= 0, "Can't specify negative number of threads"
+    if _nthreads == 0:
+        import warnings
+        warnings.warn("Zero threads in tpool.  All tpool.execute calls will\
+            execute in main thread.  Check the value of the environment \
+            variable EVENTLET_THREADPOOL_SIZE.", RuntimeWarning)
+    _reqq = Queue(maxsize=-1)
+    _rspq = Queue(maxsize=-1)
+
+    # connected socket pair
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     sock.bind(('127.0.0.1', 0))
     sock.listen(1)
     csock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     csock.connect(sock.getsockname())
+    csock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
     _wsock, _addr = sock.accept()
+    _wsock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
     sock.close()
     _rsock = greenio.GreenSocket(csock)
 
-    _reqq = Queue(maxsize=-1)
-    _rspq = Queue(maxsize=-1)
-    assert _nthreads >= 0, "Can't specify negative number of threads"
-    if _nthreads == 0:
-        import warnings
-        warnings.warn("Zero threads in tpool.  All tpool.execute calls will\
-            execute in main thread.  Check the value of the environment \
-            variable EVENTLET_THREADPOOL_SIZE.", RuntimeWarning)
     for i in six.moves.range(_nthreads):
         t = threading.Thread(target=tworker,
                              name="tpool_thread_%s" % i)
@@ -281,12 +288,20 @@ def setup():
         _threads.append(t)
 
     _coro = greenthread.spawn_n(tpool_trampoline)
+    # This yield fixes subtle error with GreenSocket.__del__
+    eventlet.sleep(0)
 
 
+# Avoid ResourceWarning unclosed socket on Python3.2+
+@atexit.register
 def killall():
     global _setup_already, _rspq, _rsock, _wsock
     if not _setup_already:
         return
+
+    # This yield fixes freeze in some scenarios
+    eventlet.sleep(0)
+
     for thr in _threads:
         _reqq.put(None)
     for thr in _threads:
@@ -294,7 +309,7 @@ def killall():
     del _threads[:]
 
     # return any remaining results
-    while not _rspq.empty():
+    while (_rspq is not None) and not _rspq.empty():
         try:
             (e, rv) = _rspq.get(block=False)
             e.send(rv)
@@ -304,10 +319,12 @@ def killall():
 
     if _coro is not None:
         greenthread.kill(_coro)
-    _rsock.close()
-    _wsock.close()
-    _rsock = None
-    _wsock = None
+    if _rsock is not None:
+        _rsock.close()
+        _rsock = None
+    if _wsock is not None:
+        _wsock.close()
+        _wsock = None
     _rspq = None
     _setup_already = False
 
index 3c93e902e2f9ab724661baf8504216d8e7837d63..9321956fd7b62e2c988f02a61b098d417a489374 100644 (file)
@@ -560,7 +560,7 @@ class RFC6455WebSocket(WebSocket):
             decoder = self.UTF8Decoder() if opcode == 1 else None
             message = self.Message(opcode, decoder=decoder)
         if not length:
-            message.push('', final=finished)
+            message.push(b'', final=finished)
         else:
             while received < length:
                 d = self.socket.recv(length - received)
index 72582770ae9edc015ea7580a5035eb7e5d1b591e..6af2b99c7ba47f609718a241ffd03c14b2766dd7 100644 (file)
@@ -1,4 +1,5 @@
 import errno
+import functools
 import os
 import sys
 import time
@@ -6,13 +7,12 @@ import traceback
 import types
 import warnings
 
-from eventlet.green import BaseHTTPServer
-from eventlet.green import socket
 from eventlet import greenio
 from eventlet import greenpool
 from eventlet import support
+from eventlet.green import BaseHTTPServer
+from eventlet.green import socket
 from eventlet.support import six
-
 from eventlet.support.six.moves import urllib
 
 
@@ -50,6 +50,10 @@ BAD_SOCK = set((errno.EBADF, 10053))
 BROKEN_SOCK = set((errno.EPIPE, errno.ECONNRESET))
 
 
+class ChunkReadError(ValueError):
+    pass
+
+
 # special flag return value for apps
 class _AlreadyHandled(object):
 
@@ -109,19 +113,18 @@ class Input(object):
         towrite.append(b'\r\n')
 
         self.wfile.writelines(towrite)
+        self.wfile.flush()
 
         # Reinitialize chunk_length (expect more data)
         self.chunk_length = -1
 
     def _do_read(self, reader, length=None):
-        if self.wfile is not None and \
-                not self.is_hundred_continue_response_sent:
+        if self.wfile is not None and not self.is_hundred_continue_response_sent:
             # 100 Continue response
             self.send_hundred_continue_response()
             self.is_hundred_continue_response_sent = True
-        if length is None and self.content_length is not None:
-            length = self.content_length - self.position
-        if length and length > self.content_length - self.position:
+        if (self.content_length is not None) and (
+                length is None or length > self.content_length - self.position):
             length = self.content_length - self.position
         if not length:
             return b''
@@ -133,8 +136,7 @@ class Input(object):
         return read
 
     def _chunked_read(self, rfile, length=None, use_readline=False):
-        if self.wfile is not None and \
-                not self.is_hundred_continue_response_sent:
+        if self.wfile is not None and not self.is_hundred_continue_response_sent:
             # 100 Continue response
             self.send_hundred_continue_response()
             self.is_hundred_continue_response_sent = True
@@ -176,7 +178,10 @@ class Input(object):
                     if use_readline and data[-1] == "\n":
                         break
                 else:
-                    self.chunk_length = int(rfile.readline().split(b";", 1)[0], 16)
+                    try:
+                        self.chunk_length = int(rfile.readline().split(b";", 1)[0], 16)
+                    except ValueError as err:
+                        raise ChunkReadError(err)
                     self.position = 0
                     if self.chunk_length == 0:
                         rfile.readline()
@@ -216,6 +221,10 @@ class Input(object):
                 for key, value in headers]
         self.hundred_continue_headers = headers
 
+    def discard(self, buffer_size=16 << 10):
+        while self.read(buffer_size):
+            pass
+
 
 class HeaderLineTooLong(Exception):
     pass
@@ -238,6 +247,9 @@ class LoggerFileWrapper(object):
         self.log = log
         self._debug = debug
 
+    def error(self, msg, *args, **kwargs):
+        self.write(msg, *args)
+
     def info(self, msg, *args, **kwargs):
         self.write(msg, *args)
 
@@ -276,9 +288,23 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
     minimum_chunk_size = MINIMUM_CHUNK_SIZE
     capitalize_response_headers = True
 
+    # https://github.com/eventlet/eventlet/issues/295
+    # Stdlib default is 0 (unbuffered), but then `wfile.writelines()` looses data
+    # so before going back to unbuffered, remove any usage of `writelines`.
+    wbufsize = 16 << 10
+
     def setup(self):
         # overriding SocketServer.setup to correctly handle SSL.Connection objects
         conn = self.connection = self.request
+
+        # TCP_QUICKACK is a better alternative to disabling Nagle's algorithm
+        # https://news.ycombinator.com/item?id=10607422
+        if getattr(socket, 'TCP_QUICKACK', None):
+            try:
+                conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_QUICKACK, True)
+            except socket.error:
+                pass
+
         try:
             self.rfile = conn.makefile('rb', self.rbufsize)
             self.wfile = conn.makefile('wb', self.wbufsize)
@@ -374,7 +400,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
         length = [0]
         status_code = [200]
 
-        def write(data, _writelines=wfile.writelines):
+        def write(data):
             towrite = []
             if not headers_set:
                 raise AssertionError("write() before start_response()")
@@ -423,7 +449,8 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
                 towrite.append(six.b("%x" % (len(data),)) + b"\r\n" + data + b"\r\n")
             else:
                 towrite.append(data)
-            _writelines(towrite)
+            wfile.writelines(towrite)
+            wfile.flush()
             length[0] = length[0] + sum(map(len, towrite))
 
         def start_response(status, response_headers, exc_info=None):
@@ -468,6 +495,8 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
                 minimum_write_chunk_size = int(self.environ.get(
                     'eventlet.minimum_write_chunk_size', self.minimum_chunk_size))
                 for data in result:
+                    if len(data) == 0:
+                        continue
                     if isinstance(data, six.text_type):
                         data = data.encode('ascii')
 
@@ -496,16 +525,20 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
         finally:
             if hasattr(result, 'close'):
                 result.close()
-            if (self.environ['eventlet.input'].chunked_input or
-                    self.environ['eventlet.input'].position
-                    < (self.environ['eventlet.input'].content_length or 0)):
+            request_input = self.environ['eventlet.input']
+            if (request_input.chunked_input or
+                    request_input.position < (request_input.content_length or 0)):
                 # Read and discard body if there was no pending 100-continue
-                if not self.environ['eventlet.input'].wfile:
-                    # NOTE: MINIMUM_CHUNK_SIZE is used here for purpose different than chunking.
-                    # We use it only cause it's at hand and has reasonable value in terms of
-                    # emptying the buffer.
-                    while self.environ['eventlet.input'].read(MINIMUM_CHUNK_SIZE):
-                        pass
+                if not request_input.wfile and self.close_connection == 0:
+                    try:
+                        request_input.discard()
+                    except ChunkReadError as e:
+                        self.close_connection = 1
+                        self.server.log.error((
+                            'chunked encoding error while discarding request body.'
+                            + ' ip={0} request="{1}" error="{2}"').format(
+                                self.get_client_ip(), self.requestline, e,
+                        ))
             finish = time.time()
 
             for hook, args, kwargs in self.environ['eventlet.posthooks']:
@@ -711,6 +744,24 @@ except ImportError:
     ACCEPT_ERRNO = set((errno.EPIPE, errno.EBADF, errno.ECONNRESET))
 
 
+def socket_repr(sock):
+    scheme = 'http'
+    if hasattr(sock, 'do_handshake'):
+        scheme = 'https'
+
+    name = sock.getsockname()
+    if sock.family == socket.AF_INET:
+        hier_part = '//{0}:{1}'.format(*name)
+    elif sock.family == socket.AF_INET6:
+        hier_part = '//[{0}]:{1}'.format(*name[:2])
+    elif sock.family == socket.AF_UNIX:
+        hier_part = name
+    else:
+        hier_part = repr(name)
+
+    return scheme + ':' + hier_part
+
+
 def server(sock, site,
            log=None,
            environ=None,
@@ -752,6 +803,7 @@ def server(sock, site,
                 If not specified, sys.stderr is used.
     :param environ: Additional parameters that go into the environ dictionary of every request.
     :param max_size: Maximum number of client connections opened at any time by this server.
+                Default is 1024.
     :param max_http_version: Set to "HTTP/1.0" to make the server pretend it only supports HTTP 1.0.
                 This can help with applications or clients that don't behave properly using HTTP 1.1.
     :param protocol: Protocol class.  Deprecated.
@@ -805,19 +857,8 @@ def server(sock, site,
     else:
         pool = greenpool.GreenPool(max_size)
     try:
-        host, port = sock.getsockname()[:2]
-        port = ':%s' % (port, )
-        if hasattr(sock, 'do_handshake'):
-            scheme = 'https'
-            if port == ':443':
-                port = ''
-        else:
-            scheme = 'http'
-            if port == ':80':
-                port = ''
-
-        serv.log.info("(%s) wsgi starting up on %s://%s%s/" % (
-            serv.pid, scheme, host, port))
+        serv.log.info("(%s) wsgi starting up on %s" % (
+            serv.pid, socket_repr(sock)))
         while is_accepting:
             try:
                 client_socket = sock.accept()
index 26c0c2ec5ab49c5102314dcb7991e9dece9b0bf1..0c37cdde7bf45e9155e21fe613c57852ddb66a5a 100644 (file)
@@ -209,6 +209,13 @@ def check_idle_cpu_usage(duration, allowed_part):
     r2 = resource.getrusage(resource.RUSAGE_SELF)
     utime = r2.ru_utime - r1.ru_utime
     stime = r2.ru_stime - r1.ru_stime
+
+    # This check is reliably unreliable on Travis, presumably because of CPU
+    # resources being quite restricted by the build environment. The workaround
+    # is to apply an arbitrary factor that should be enough to make it work nicely.
+    if os.environ.get('TRAVIS') == 'true':
+        allowed_part *= 1.2
+
     assert utime + stime < duration * allowed_part, \
         "CPU usage over limit: user %.0f%% sys %.0f%% allowed %.0f%%" % (
             utime / duration * 100, stime / duration * 100,
@@ -292,15 +299,22 @@ def get_database_auth():
     return retval
 
 
-def run_python(path):
-    if not path.endswith('.py'):
-        path += '.py'
-    path = os.path.abspath(path)
-    src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+def run_python(path, env=None, args=None):
+    new_argv = [sys.executable]
     new_env = os.environ.copy()
-    new_env['PYTHONPATH'] = os.pathsep.join(sys.path + [src_dir])
+    if path:
+        if not path.endswith('.py'):
+            path += '.py'
+        path = os.path.abspath(path)
+        new_argv.append(path)
+        src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+        new_env['PYTHONPATH'] = os.pathsep.join(sys.path + [src_dir])
+    if env:
+        new_env.update(env)
+    if args:
+        new_argv.extend(args)
     p = subprocess.Popen(
-        [sys.executable, path],
+        new_argv,
         env=new_env,
         stderr=subprocess.STDOUT,
         stdin=subprocess.PIPE,
@@ -310,15 +324,18 @@ def run_python(path):
     return output
 
 
-def run_isolated(path, prefix='tests/isolated/'):
-    output = run_python(prefix + path).rstrip()
+def run_isolated(path, prefix='tests/isolated/', env=None, args=None):
+    output = run_python(prefix + path, env=env, args=args).rstrip()
     if output.startswith(b'skip'):
         parts = output.split(b':', 1)
         skip_args = []
         if len(parts) > 1:
             skip_args.append(parts[1])
         raise SkipTest(*skip_args)
-    assert output == b'pass', output
+    ok = output == b'pass'
+    if not ok:
+        sys.stderr.write('Isolated test {0} output:\n---\n{1}\n---\n'.format(path, output.decode()))
+    assert ok, 'Expected single line "pass" in stdout'
 
 
 certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt')
index 6facffeec2f751e0e3115411fa1713b36a8a96f1..f932eb04c07425aaa6cb03be4f41389890dd0dde 100644 (file)
@@ -1,4 +1,8 @@
+import os
+import os.path
+
 import eventlet
+
 from eventlet import backdoor
 from eventlet.green import socket
 
@@ -13,6 +17,9 @@ class BackdoorTest(LimitedTestCase):
         serv = eventlet.spawn(backdoor.backdoor_server, listener)
         client = socket.socket()
         client.connect(('localhost', listener.getsockname()[1]))
+        self._run_test_on_client_and_server(client, serv)
+
+    def _run_test_on_client_and_server(self, client, server_thread):
         f = client.makefile('rw')
         assert 'Python' in f.readline()
         f.readline()  # build info
@@ -25,10 +32,31 @@ class BackdoorTest(LimitedTestCase):
         self.assertEqual('>>> ', f.read(4))
         f.close()
         client.close()
-        serv.kill()
+        server_thread.kill()
         # wait for the console to discover that it's dead
         eventlet.sleep(0.1)
 
+    def test_server_on_ipv6_socket(self):
+        listener = socket.socket(socket.AF_INET6)
+        listener.bind(('::1', 0))
+        listener.listen(5)
+        serv = eventlet.spawn(backdoor.backdoor_server, listener)
+        client = socket.socket(socket.AF_INET6)
+        client.connect(listener.getsockname())
+        self._run_test_on_client_and_server(client, serv)
+
+    def test_server_on_unix_socket(self):
+        SOCKET_PATH = '/tmp/eventlet_backdoor_test.socket'
+        if os.path.exists(SOCKET_PATH):
+            os.unlink(SOCKET_PATH)
+        listener = socket.socket(socket.AF_UNIX)
+        listener.bind(SOCKET_PATH)
+        listener.listen(5)
+        serv = eventlet.spawn(backdoor.backdoor_server, listener)
+        client = socket.socket(socket.AF_UNIX)
+        client.connect(SOCKET_PATH)
+        self._run_test_on_client_and_server(client, serv)
+
 
 if __name__ == '__main__':
     main()
index 9fc9ebc2005232b80486d368c3be116341cd6eb7..da72be44057817533616719d94df39a3d1bed759 100644 (file)
@@ -7,7 +7,7 @@ import os
 import traceback
 from unittest import TestCase, main
 
-from tests import mock, skipped, skip_unless, skip_with_pyevent, get_database_auth
+from tests import mock, skip_unless, skip_with_pyevent, get_database_auth
 from eventlet import event
 from eventlet import db_pool
 from eventlet.support import six
@@ -116,14 +116,6 @@ class DBConnectionPool(DBTester):
         assert self.pool.free() == 1
         self.assertRaises(AttributeError, self.connection.cursor)
 
-    @skipped
-    def test_deletion_does_a_put(self):
-        # doing a put on del causes some issues if __del__ is called in the
-        # main coroutine, so, not doing that for now
-        assert self.pool.free() == 0
-        self.connection = None
-        assert self.pool.free() == 1
-
     def test_put_doesnt_double_wrap(self):
         self.pool.put(self.connection)
         conn = self.pool.get()
@@ -213,45 +205,6 @@ class DBConnectionPool(DBTester):
             conn.commit()
             self.pool.put(conn)
 
-    @skipped
-    def test_two_simultaneous_connections(self):
-        # timing-sensitive test, disabled until we come up with a better
-        # way to do this
-        self.pool = self.create_pool(max_size=2)
-        conn = self.pool.get()
-        self.set_up_dummy_table(conn)
-        self.fill_up_table(conn)
-        curs = conn.cursor()
-        conn2 = self.pool.get()
-        self.set_up_dummy_table(conn2)
-        self.fill_up_table(conn2)
-        curs2 = conn2.cursor()
-        results = []
-        LONG_QUERY = "select * from test_table"
-        SHORT_QUERY = "select * from test_table where row_id <= 20"
-
-        evt = event.Event()
-
-        def long_running_query():
-            self.assert_cursor_works(curs)
-            curs.execute(LONG_QUERY)
-            results.append(1)
-            evt.send()
-        evt2 = event.Event()
-
-        def short_running_query():
-            self.assert_cursor_works(curs2)
-            curs2.execute(SHORT_QUERY)
-            results.append(2)
-            evt2.send()
-
-        eventlet.spawn(long_running_query)
-        eventlet.spawn(short_running_query)
-        evt.wait()
-        evt2.wait()
-        results.sort()
-        self.assertEqual([1, 2], results)
-
     def test_clear(self):
         self.pool = self.create_pool()
         self.pool.put(self.connection)
@@ -318,80 +271,6 @@ class DBConnectionPool(DBTester):
         self.connection.close()
         self.assertEqual(len(self.pool.free_items), 0)
 
-    @skipped
-    def test_max_idle(self):
-        # This test is timing-sensitive.  Rename the function without
-        # the "dont" to run it, but beware that it could fail or take
-        # a while.
-
-        self.pool = self.create_pool(max_size=2, max_idle=0.02)
-        self.connection = self.pool.get()
-        self.connection.close()
-        self.assertEqual(len(self.pool.free_items), 1)
-        eventlet.sleep(0.01)  # not long enough to trigger the idle timeout
-        self.assertEqual(len(self.pool.free_items), 1)
-        self.connection = self.pool.get()
-        self.connection.close()
-        self.assertEqual(len(self.pool.free_items), 1)
-        eventlet.sleep(0.01)  # idle timeout should have fired but done nothing
-        self.assertEqual(len(self.pool.free_items), 1)
-        self.connection = self.pool.get()
-        self.connection.close()
-        self.assertEqual(len(self.pool.free_items), 1)
-        eventlet.sleep(0.03)  # long enough to trigger idle timeout for real
-        self.assertEqual(len(self.pool.free_items), 0)
-
-    @skipped
-    def test_max_idle_many(self):
-        # This test is timing-sensitive.  Rename the function without
-        # the "dont" to run it, but beware that it could fail or take
-        # a while.
-
-        self.pool = self.create_pool(max_size=2, max_idle=0.02)
-        self.connection, conn2 = self.pool.get(), self.pool.get()
-        self.connection.close()
-        eventlet.sleep(0.01)
-        self.assertEqual(len(self.pool.free_items), 1)
-        conn2.close()
-        self.assertEqual(len(self.pool.free_items), 2)
-        eventlet.sleep(0.02)  # trigger cleanup of conn1 but not conn2
-        self.assertEqual(len(self.pool.free_items), 1)
-
-    @skipped
-    def test_max_age(self):
-        # This test is timing-sensitive.  Rename the function without
-        # the "dont" to run it, but beware that it could fail or take
-        # a while.
-
-        self.pool = self.create_pool(max_size=2, max_age=0.05)
-        self.connection = self.pool.get()
-        self.connection.close()
-        self.assertEqual(len(self.pool.free_items), 1)
-        eventlet.sleep(0.01)  # not long enough to trigger the age timeout
-        self.assertEqual(len(self.pool.free_items), 1)
-        self.connection = self.pool.get()
-        self.connection.close()
-        self.assertEqual(len(self.pool.free_items), 1)
-        eventlet.sleep(0.05)  # long enough to trigger age timeout
-        self.assertEqual(len(self.pool.free_items), 0)
-
-    @skipped
-    def test_max_age_many(self):
-        # This test is timing-sensitive.  Rename the function without
-        # the "dont" to run it, but beware that it could fail or take
-        # a while.
-
-        self.pool = self.create_pool(max_size=2, max_age=0.15)
-        self.connection, conn2 = self.pool.get(), self.pool.get()
-        self.connection.close()
-        self.assertEqual(len(self.pool.free_items), 1)
-        eventlet.sleep(0)  # not long enough to trigger the age timeout
-        self.assertEqual(len(self.pool.free_items), 1)
-        eventlet.sleep(0.2)  # long enough to trigger age timeout
-        self.assertEqual(len(self.pool.free_items), 0)
-        conn2.close()  # should not be added to the free items
-        self.assertEqual(len(self.pool.free_items), 0)
-
     def test_waiters_get_woken(self):
         # verify that when there's someone waiting on an empty pool
         # and someone puts an immediately-closed connection back in
@@ -421,29 +300,6 @@ class DBConnectionPool(DBTester):
         self.assertEqual(self.pool.waiting(), 0)
         self.pool.put(conn)
 
-    @skipped
-    def test_0_straight_benchmark(self):
-        """ Benchmark; don't run unless you want to wait a while."""
-        import time
-        iterations = 20000
-        c = self.connection.cursor()
-        self.connection.commit()
-
-        def bench(c):
-            for i in six.moves.range(iterations):
-                c.execute('select 1')
-
-        bench(c)  # warm-up
-        results = []
-        for i in range(3):
-            start = time.time()
-            bench(c)
-            end = time.time()
-            results.append(end - start)
-
-        print("\n%u iterations took an average of %f seconds, (%s) in %s\n" % (
-            iterations, sum(results) / len(results), results, type(self)))
-
     def test_raising_create(self):
         # if the create() method raises an exception the pool should
         # not lose any connections
index f8931c1dc66b8e91e2910f56a6d1f0f0f1b89ffd..fb7a58b949f83a6294dfcd74e30bf5a0f86e1044 100644 (file)
-import os
-from eventlet.support import six
-from tests.patcher_test import ProcessBase
-from tests import skip_with_pyevent
+import tests
 
 
-class Socket(ProcessBase):
-    def test_patched_thread(self):
-        new_mod = """from eventlet.green import socket
-socket.gethostbyname('localhost')
-socket.getaddrinfo('localhost', 80)
-"""
-        os.environ['EVENTLET_TPOOL_DNS'] = 'yes'
-        try:
-            self.write_to_tempfile("newmod", new_mod)
-            output, lines = self.launch_subprocess('newmod.py')
-            self.assertEqual(len(lines), 1, lines)
-        finally:
-            del os.environ['EVENTLET_TPOOL_DNS']
-
+def test_hub_selects():
+    code = 'from eventlet import hubs\nprint(hubs.get_hub())'
+    output = tests.run_python(
+        path=None,
+        env={'EVENTLET_HUB': 'selects'},
+        args=['-c', code],
+    )
+    assert output.count(b'\n') == 1
+    assert b'eventlet.hubs.selects.Hub' in output
 
-class Tpool(ProcessBase):
-    @skip_with_pyevent
-    def test_tpool_size(self):
-        expected = "40"
-        normal = "20"
-        new_mod = """from eventlet import tpool
-import eventlet
-import time
-current = [0]
-highwater = [0]
-def count():
-    current[0] += 1
-    time.sleep(0.1)
-    if current[0] > highwater[0]:
-        highwater[0] = current[0]
-    current[0] -= 1
-expected = %s
-normal = %s
-p = eventlet.GreenPool()
-for i in range(expected*2):
-    p.spawn(tpool.execute, count)
-p.waitall()
-assert highwater[0] > 20, "Highwater %%s  <= %%s" %% (highwater[0], normal)
-"""
-        os.environ['EVENTLET_THREADPOOL_SIZE'] = expected
-        try:
-            self.write_to_tempfile("newmod", new_mod % (expected, normal))
-            output, lines = self.launch_subprocess('newmod.py')
-            self.assertEqual(len(lines), 1, lines)
-        finally:
-            del os.environ['EVENTLET_THREADPOOL_SIZE']
 
-    def test_tpool_negative(self):
-        new_mod = """from eventlet import tpool
-import eventlet
-import time
-def do():
-    print("should not get here")
-try:
-    tpool.execute(do)
-except AssertionError:
-    print("success")
-"""
-        os.environ['EVENTLET_THREADPOOL_SIZE'] = "-1"
-        try:
-            self.write_to_tempfile("newmod", new_mod)
-            output, lines = self.launch_subprocess('newmod.py')
-            self.assertEqual(len(lines), 2, lines)
-            self.assertEqual(lines[0], "success", output)
-        finally:
-            del os.environ['EVENTLET_THREADPOOL_SIZE']
+def test_tpool_dns():
+    code = '''\
+from eventlet.green import socket
+socket.gethostbyname('localhost')
+socket.getaddrinfo('localhost', 80)
+print('pass')
+'''
+    output = tests.run_python(
+        path=None,
+        env={'EVENTLET_TPOOL_DNS': 'yes'},
+        args=['-c', code],
+    )
+    assert output.rstrip() == b'pass'
 
-    def test_tpool_zero(self):
-        new_mod = """from eventlet import tpool
-import eventlet
-import time
-def do():
-    print("ran it")
-tpool.execute(do)
-"""
-        os.environ['EVENTLET_THREADPOOL_SIZE'] = "0"
-        try:
-            self.write_to_tempfile("newmod", new_mod)
-            output, lines = self.launch_subprocess('newmod.py')
-            self.assertEqual(len(lines), 4, lines)
-            self.assertEqual(lines[-2], 'ran it', lines)
-            assert 'Warning' in lines[1] or 'Warning' in lines[0], lines
-        finally:
-            del os.environ['EVENTLET_THREADPOOL_SIZE']
 
+@tests.skip_with_pyevent
+def test_tpool_size():
+    expected = '40'
+    normal = '20'
+    tests.run_isolated(
+        path='env_tpool_size.py',
+        env={'EVENTLET_THREADPOOL_SIZE': expected},
+        args=[expected, normal],
+    )
 
-class Hub(ProcessBase):
 
-    def setUp(self):
-        super(Hub, self).setUp()
-        self.old_environ = os.environ.get('EVENTLET_HUB')
-        os.environ['EVENTLET_HUB'] = 'selects'
+def test_tpool_negative():
+    tests.run_isolated('env_tpool_negative.py', env={'EVENTLET_THREADPOOL_SIZE': '-1'})
 
-    def tearDown(self):
-        if self.old_environ:
-            os.environ['EVENTLET_HUB'] = self.old_environ
-        else:
-            del os.environ['EVENTLET_HUB']
-        super(Hub, self).tearDown()
 
-    def test_eventlet_hub(self):
-        new_mod = """from eventlet import hubs
-print(hubs.get_hub())
-"""
-        self.write_to_tempfile("newmod", new_mod)
-        output, lines = self.launch_subprocess('newmod.py')
-        self.assertEqual(len(lines), 2, "\n".join(lines))
-        assert "selects" in lines[0]
+def test_tpool_zero():
+    tests.run_isolated('env_tpool_zero.py', env={'EVENTLET_THREADPOOL_SIZE': '0'})
index 8a94b7bddd80d1b1d45460ddac34e0f15192260d..99119b37bc1d4abfe7aee0897e11850b1a32e83e 100644 (file)
@@ -3,6 +3,7 @@ import errno
 import eventlet
 import fcntl
 import gc
+from io import DEFAULT_BUFFER_SIZE
 import os
 import shutil
 import socket as _orig_sock
@@ -11,6 +12,7 @@ import tempfile
 
 from nose.tools import eq_
 
+import eventlet
 from eventlet import event, greenio, debug
 from eventlet.hubs import get_hub
 from eventlet.green import select, socket, time, ssl
@@ -18,10 +20,6 @@ from eventlet.support import capture_stderr, get_errno, six
 import tests
 
 
-if six.PY3:
-    buffer = memoryview
-
-
 def bufsized(sock, size=1):
     """ Resize both send and receive buffers on a socket.
     Useful for testing trampoline.  Returns the socket.
@@ -34,6 +32,15 @@ def bufsized(sock, size=1):
     return sock
 
 
+def expect_socket_timeout(function, *args):
+    try:
+        function(*args)
+        raise AssertionError("socket.timeout not raised")
+    except socket.timeout as e:
+        assert hasattr(e, 'args')
+        eq_(e.args[0], 'timed out')
+
+
 def min_buf_size():
     """Return the minimum buffer size that the platform supports."""
     test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -71,12 +78,9 @@ class TestGreenSocket(tests.LimitedTestCase):
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         s.settimeout(0.1)
         gs = greenio.GreenSocket(s)
+
         try:
-            gs.connect(('192.0.2.1', 80))
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+            expect_socket_timeout(gs.connect, ('192.0.2.1', 80))
         except socket.error as e:
             # unreachable is also a valid outcome
             if not get_errno(e) in (errno.EHOSTUNREACH, errno.ENETUNREACH):
@@ -89,12 +93,7 @@ class TestGreenSocket(tests.LimitedTestCase):
 
         s.settimeout(0.1)
         gs = greenio.GreenSocket(s)
-        try:
-            gs.accept()
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+        expect_socket_timeout(gs.accept)
 
     def test_connect_ex_timeout(self):
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -125,12 +124,8 @@ class TestGreenSocket(tests.LimitedTestCase):
 
         client.connect(addr)
 
-        try:
-            client.recv(8192)
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+        expect_socket_timeout(client.recv, 0)
+        expect_socket_timeout(client.recv, 8192)
 
         evt.send()
         gt.wait()
@@ -141,12 +136,8 @@ class TestGreenSocket(tests.LimitedTestCase):
         gs.settimeout(.1)
         gs.bind(('', 0))
 
-        try:
-            gs.recvfrom(8192)
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+        expect_socket_timeout(gs.recvfrom, 0)
+        expect_socket_timeout(gs.recvfrom, 8192)
 
     def test_recvfrom_into_timeout(self):
         buf = array.array('B')
@@ -156,12 +147,7 @@ class TestGreenSocket(tests.LimitedTestCase):
         gs.settimeout(.1)
         gs.bind(('', 0))
 
-        try:
-            gs.recvfrom_into(buf)
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+        expect_socket_timeout(gs.recvfrom_into, buf)
 
     def test_recv_into_timeout(self):
         buf = array.array('B')
@@ -186,12 +172,7 @@ class TestGreenSocket(tests.LimitedTestCase):
 
         client.connect(addr)
 
-        try:
-            client.recv_into(buf)
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+        expect_socket_timeout(client.recv_into, buf)
 
         evt.send()
         gt.wait()
@@ -214,19 +195,17 @@ class TestGreenSocket(tests.LimitedTestCase):
 
         client = bufsized(greenio.GreenSocket(socket.socket()))
         client.connect(addr)
-        try:
-            client.settimeout(0.00001)
-            msg = b"A" * 100000  # large enough number to overwhelm most buffers
 
-            total_sent = 0
-            # want to exceed the size of the OS buffer so it'll block in a
-            # single send
+        client.settimeout(0.00001)
+        msg = b"A" * 100000  # large enough number to overwhelm most buffers
+
+        # want to exceed the size of the OS buffer so it'll block in a
+        # single send
+        def send():
             for x in range(10):
-                total_sent += client.send(msg)
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+                client.send(msg)
+
+        expect_socket_timeout(send)
 
         evt.send()
         gt.wait()
@@ -251,15 +230,9 @@ class TestGreenSocket(tests.LimitedTestCase):
         client.settimeout(0.1)
         client.connect(addr)
 
-        try:
-            msg = b"A" * (8 << 20)
-
-            # want to exceed the size of the OS buffer so it'll block
-            client.sendall(msg)
-            self.fail("socket.timeout not raised")
-        except socket.timeout as e:
-            assert hasattr(e, 'args')
-            self.assertEqual(e.args[0], 'timed out')
+        # want to exceed the size of the OS buffer so it'll block
+        msg = b"A" * (8 << 20)
+        expect_socket_timeout(client.sendall, msg)
 
         evt.send()
         gt.wait()
@@ -509,6 +482,9 @@ class TestGreenSocket(tests.LimitedTestCase):
             while True:
                 try:
                     sock.sendall(b'hello world')
+                    # Arbitrary delay to not use all available CPU, keeps the test
+                    # running quickly and reliably under a second
+                    time.sleep(0.001)
                 except socket.error as e:
                     if get_errno(e) == errno.EPIPE:
                         return
@@ -524,6 +500,9 @@ class TestGreenSocket(tests.LimitedTestCase):
                 while True:
                     data = client.recv(1024)
                     assert data
+                    # Arbitrary delay to not use all available CPU, keeps the test
+                    # running quickly and reliably under a second
+                    time.sleep(0.001)
             except socket.error as e:
                 # we get an EBADF because client is closed in the same process
                 # (but a different greenthread)
@@ -615,6 +594,21 @@ class TestGreenSocket(tests.LimitedTestCase):
         # should not raise
         greenio.shutdown_safe(sock)
 
+    def test_datagram_socket_operations_work(self):
+        receiver = greenio.GreenSocket(socket.AF_INET, socket.SOCK_DGRAM)
+        receiver.bind(('127.0.0.1', 0))
+        address = receiver.getsockname()
+
+        sender = greenio.GreenSocket(socket.AF_INET, socket.SOCK_DGRAM)
+
+        # Two ways sendto can be called
+        sender.sendto(b'first', address)
+        sender.sendto(b'second', 0, address)
+
+        sender_address = ('127.0.0.1', sender.getsockname()[1])
+        eq_(receiver.recvfrom(1024), (b'first', sender_address))
+        eq_(receiver.recvfrom(1024), (b'second', sender_address))
+
 
 def test_get_fileno_of_a_socket_works():
     class DummySocket(object):
@@ -711,6 +705,36 @@ class TestGreenPipe(tests.LimitedTestCase):
 
         gt.wait()
 
+    def test_pip_read_until_end(self):
+        # similar to test_pip_read above but reading until eof
+        r, w = os.pipe()
+
+        r = greenio.GreenPipe(r, 'rb')
+        w = greenio.GreenPipe(w, 'wb')
+
+        w.write(b'c' * DEFAULT_BUFFER_SIZE * 2)
+        w.close()
+
+        buf = r.read()  # no chunk size specified; read until end
+        self.assertEqual(len(buf), 2 * DEFAULT_BUFFER_SIZE)
+        self.assertEqual(buf[:3], b'ccc')
+
+    def test_pipe_read_unbuffered(self):
+        # Ensure that seting the buffer size works properly on GreenPipes,
+        # it used to be ignored on Python 2 and the test would hang on r.readline()
+        # below.
+        r, w = os.pipe()
+
+        r = greenio.GreenPipe(r, 'rb', 0)
+        w = greenio.GreenPipe(w, 'wb', 0)
+
+        w.write(b'line\n')
+
+        line = r.readline()
+        self.assertEqual(line, b'line\n')
+        r.close()
+        w.close()
+
     def test_pipe_writes_large_messages(self):
         r, w = os.pipe()
 
@@ -769,7 +793,7 @@ class TestGreenIoLong(tests.LimitedTestCase):
     TEST_TIMEOUT = 10  # the test here might take a while depending on the OS
 
     @tests.skip_with_pyevent
-    def test_multiple_readers(self, clibufsize=False):
+    def test_multiple_readers(self):
         debug.hub_prevent_multiple_readers(False)
         recvsize = 2 * min_buf_size()
         sendsize = 10 * recvsize
@@ -809,11 +833,15 @@ class TestGreenIoLong(tests.LimitedTestCase):
         server_coro = eventlet.spawn(server)
         client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         client.connect(('127.0.0.1', listener.getsockname()[1]))
-        if clibufsize:
-            bufsized(client, size=sendsize)
-        else:
-            bufsized(client)
-        client.sendall(b'*' * sendsize)
+        bufsized(client, size=sendsize)
+
+        # Split into multiple chunks so that we can wait a little
+        # every iteration which allows both readers to queue and
+        # recv some data when we actually send it.
+        for i in range(20):
+            eventlet.sleep(0.001)
+            client.sendall(b'*' * (sendsize // 20))
+
         client.close()
         server_coro.wait()
         listener.close()
@@ -821,110 +849,6 @@ class TestGreenIoLong(tests.LimitedTestCase):
         assert len(results2) > 0
         debug.hub_prevent_multiple_readers()
 
-    @tests.skipped  # by rdw because it fails but it's not clear how to make it pass
-    @tests.skip_with_pyevent
-    def test_multiple_readers2(self):
-        self.test_multiple_readers(clibufsize=True)
-
-
-class TestGreenIoStarvation(tests.LimitedTestCase):
-    # fixme: this doesn't succeed, because of eventlet's predetermined
-    # ordering.  two processes, one with server, one with client eventlets
-    # might be more reliable?
-
-    TEST_TIMEOUT = 300  # the test here might take a while depending on the OS
-
-    @tests.skipped  # by rdw, because it fails but it's not clear how to make it pass
-    @tests.skip_with_pyevent
-    def test_server_starvation(self, sendloops=15):
-        recvsize = 2 * min_buf_size()
-        sendsize = 10000 * recvsize
-
-        results = [[] for i in range(5)]
-
-        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        listener.bind(('127.0.0.1', 0))
-        port = listener.getsockname()[1]
-        listener.listen(50)
-
-        base_time = time.time()
-
-        def server(my_results):
-            sock, addr = listener.accept()
-
-            datasize = 0
-
-            t1 = None
-            t2 = None
-            try:
-                while True:
-                    data = sock.recv(recvsize)
-                    if not t1:
-                        t1 = time.time() - base_time
-                    if not data:
-                        t2 = time.time() - base_time
-                        my_results.append(datasize)
-                        my_results.append((t1, t2))
-                        break
-                    datasize += len(data)
-            finally:
-                sock.close()
-
-        def client():
-            pid = os.fork()
-            if pid:
-                return pid
-
-            client = _orig_sock.socket(socket.AF_INET, socket.SOCK_STREAM)
-            client.connect(('127.0.0.1', port))
-
-            bufsized(client, size=sendsize)
-
-            for i in range(sendloops):
-                client.sendall(b'*' * sendsize)
-            client.close()
-            os._exit(0)
-
-        clients = []
-        servers = []
-        for r in results:
-            servers.append(eventlet.spawn(server, r))
-        for r in results:
-            clients.append(client())
-
-        for s in servers:
-            s.wait()
-        for c in clients:
-            os.waitpid(c, 0)
-
-        listener.close()
-
-        # now test that all of the server receive intervals overlap, and
-        # that there were no errors.
-        for r in results:
-            assert len(r) == 2, "length is %d not 2!: %s\n%s" % (len(r), r, results)
-            assert r[0] == sendsize * sendloops
-            assert len(r[1]) == 2
-            assert r[1][0] is not None
-            assert r[1][1] is not None
-
-        starttimes = sorted(r[1][0] for r in results)
-        endtimes = sorted(r[1][1] for r in results)
-        runlengths = sorted(r[1][1] - r[1][0] for r in results)
-
-        # assert that the last task started before the first task ended
-        # (our no-starvation condition)
-        assert starttimes[-1] < endtimes[0], \
-            "Not overlapping: starts %s ends %s" % (starttimes, endtimes)
-
-        maxstartdiff = starttimes[-1] - starttimes[0]
-
-        assert maxstartdiff * 2 < runlengths[0], \
-            "Largest difference in starting times more than twice the shortest running time!"
-        assert runlengths[0] * 2 > runlengths[-1], \
-            "Longest runtime more than twice as long as shortest!"
-
 
 def test_set_nonblocking():
     sock = _orig_sock.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -954,3 +878,76 @@ def test_socket_del_fails_gracefully_when_not_fully_initialized():
 
 def test_double_close_219():
     tests.run_isolated('greenio_double_close_219.py')
+
+
+def test_partial_write_295():
+    # https://github.com/eventlet/eventlet/issues/295
+    # `socket.makefile('w').writelines()` must send all
+    # despite partial writes by underlying socket
+    listen_socket = eventlet.listen(('localhost', 0))
+    original_accept = listen_socket.accept
+
+    def talk(conn):
+        f = conn.makefile('wb')
+        line = b'*' * 2140
+        f.writelines([line] * 10000)
+        conn.close()
+
+    def accept():
+        connection, address = original_accept()
+        original_send = connection.send
+
+        def slow_send(b, *args):
+            b = b[:1031]
+            return original_send(b, *args)
+
+        connection.send = slow_send
+        eventlet.spawn(talk, connection)
+        return connection, address
+
+    listen_socket.accept = accept
+
+    eventlet.spawn(listen_socket.accept)
+    sock = eventlet.connect(listen_socket.getsockname())
+    with eventlet.Timeout(10):
+        bs = sock.makefile('rb').read()
+    assert len(bs) == 21400000
+    assert bs == (b'*' * 21400000)
+
+
+def test_socket_file_read_non_int():
+    listen_socket = eventlet.listen(('localhost', 0))
+
+    def server():
+        conn, _ = listen_socket.accept()
+        conn.recv(1)
+        conn.sendall('response')
+        conn.close()
+
+    eventlet.spawn(server)
+    sock = eventlet.connect(listen_socket.getsockname())
+
+    fd = sock.makefile('rwb')
+    fd.write(b'?')
+    fd.flush()
+    with eventlet.Timeout(1):
+        try:
+            fd.read("This shouldn't work")
+            assert False
+        except TypeError:
+            pass
+
+
+def test_pipe_context():
+    # ensure using a pipe as a context actually closes it.
+    r, w = os.pipe()
+    r = greenio.GreenPipe(r)
+    w = greenio.GreenPipe(w, 'w')
+
+    with r:
+        pass
+    assert r.closed and not w.closed
+
+    with w as f:
+        assert f == w
+    assert r.closed and w.closed
diff --git a/python-eventlet/tests/greenpipe_test_with_statement.py b/python-eventlet/tests/greenpipe_test_with_statement.py
deleted file mode 100644 (file)
index c0491b3..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-from __future__ import with_statement
-
-import os
-
-from eventlet import greenio
-from tests import LimitedTestCase
-
-
-class TestGreenPipeWithStatement(LimitedTestCase):
-    def test_pipe_context(self):
-        # ensure using a pipe as a context actually closes it.
-        r, w = os.pipe()
-
-        r = greenio.GreenPipe(r)
-        w = greenio.GreenPipe(w, 'w')
-
-        with r:
-            pass
-
-        assert r.closed and not w.closed
-
-        with w as f:
-            assert f == w
-
-        assert r.closed and w.closed
diff --git a/python-eventlet/tests/isolated/env_tpool_negative.py b/python-eventlet/tests/isolated/env_tpool_negative.py
new file mode 100644 (file)
index 0000000..82c02d4
--- /dev/null
@@ -0,0 +1,11 @@
+__test__ = False
+
+if __name__ == '__main__':
+    from eventlet import tpool
+
+    def do():
+        print("should not get here")
+    try:
+        tpool.execute(do)
+    except AssertionError:
+        print('pass')
diff --git a/python-eventlet/tests/isolated/env_tpool_size.py b/python-eventlet/tests/isolated/env_tpool_size.py
new file mode 100644 (file)
index 0000000..a34a9c7
--- /dev/null
@@ -0,0 +1,26 @@
+__test__ = False
+
+if __name__ == '__main__':
+    import sys
+    import time
+    from eventlet import tpool
+    import eventlet
+
+    current = [0]
+    highwater = [0]
+
+    def count():
+        current[0] += 1
+        time.sleep(0.01)
+        if current[0] > highwater[0]:
+            highwater[0] = current[0]
+        current[0] -= 1
+
+    expected = int(sys.argv[1])
+    normal = int(sys.argv[2])
+    p = eventlet.GreenPool()
+    for i in range(expected * 2):
+        p.spawn(tpool.execute, count)
+    p.waitall()
+    assert highwater[0] > normal, "Highwater %s <= %s" % (highwater[0], normal)
+    print('pass')
diff --git a/python-eventlet/tests/isolated/env_tpool_zero.py b/python-eventlet/tests/isolated/env_tpool_zero.py
new file mode 100644 (file)
index 0000000..13fad9c
--- /dev/null
@@ -0,0 +1,22 @@
+__test__ = False
+
+if __name__ == '__main__':
+    import warnings
+    from eventlet import tpool
+    g = [False]
+
+    def do():
+        g[0] = True
+
+    with warnings.catch_warnings(record=True) as ws:
+        warnings.simplefilter('always')
+
+        tpool.execute(do)
+
+        assert len(ws) == 1
+        msg = str(ws[0].message)
+        assert 'Zero threads in tpool' in msg
+        assert 'EVENTLET_THREADPOOL_SIZE' in msg
+
+    assert g[0]
+    print('pass')
index 5c8bb14b262d8105a920992263fdf13cba408540..dbf5899e3504a2e8abedb209d1cb4aebd8c3d69e 100644 (file)
@@ -1,16 +1,13 @@
 __test__ = False
 
-
-def main():
+if __name__ == '__main__':
+    import sys
     import eventlet
     try:
         from dns import reversename
     except ImportError:
         print('skip:require dns (package dnspython)')
-        return
+        sys.exit(1)
     eventlet.monkey_patch(all=True)
     reversename.from_address('127.0.0.1')
     print('pass')
-
-if __name__ == '__main__':
-    main()
index 9881ce848652260a2cf7756f4bd9c04b9b396cce..ab27d19c6a1c5a8f1aad43211ce193802e26e7eb 100644 (file)
@@ -1,7 +1,6 @@
 __test__ = False
 
-
-def main():
+if __name__ == '__main__':
     import eventlet
     eventlet.monkey_patch()
     import subprocess
@@ -17,6 +16,3 @@ def main():
 
     f.close() # OSError, because the fd 3 has already been closed
     print('pass')
-
-if __name__ == '__main__':
-    main()
index 8522d064f2ad380e782c83e34048e1388ac2328f..da1e0744fe7c5c82ff3a8e5cd721629d5e6d3460 100644 (file)
@@ -1,6 +1,3 @@
-from __future__ import print_function
-
-# no standard tests in this file, ignore
 __test__ = False
 
 if __name__ == '__main__':
diff --git a/python-eventlet/tests/isolated/patcher_blocking_select_methods_are_deleted.py b/python-eventlet/tests/isolated/patcher_blocking_select_methods_are_deleted.py
new file mode 100644 (file)
index 0000000..67da6a5
--- /dev/null
@@ -0,0 +1,34 @@
+__test__ = False
+
+if __name__ == '__main__':
+    import eventlet
+    eventlet.monkey_patch()
+
+    # Leaving unpatched select methods in the select module is a recipe
+    # for trouble and this test makes sure we don't do that.
+    #
+    # Issues:
+    # * https://bitbucket.org/eventlet/eventlet/issues/167
+    # * https://github.com/eventlet/eventlet/issues/169
+    import select
+    # FIXME: must also delete `poll`, but it breaks subprocess `communicate()`
+    # https://github.com/eventlet/eventlet/issues/290
+    for name in ['devpoll', 'epoll', 'kqueue', 'kevent']:
+        assert not hasattr(select, name), name
+
+    import sys
+
+    if sys.version_info >= (3, 4):
+        import selectors
+        for name in [
+            'PollSelector',
+            'EpollSelector',
+            'DevpollSelector',
+            'KqueueSelector',
+        ]:
+            assert not hasattr(selectors, name), name
+
+        default = selectors.DefaultSelector
+        assert default is selectors.SelectSelector, default
+
+    print('pass')
index b6e5a11c25b10e4aaf576fbef35ca79d89fa4110..922f8578f83e69572450096b3382422290fc56bd 100644 (file)
@@ -1,11 +1,3 @@
-from __future__ import print_function
-
-import sys
-
-import eventlet
-
-
-# no standard tests in this file, ignore
 __test__ = False
 
 
@@ -14,6 +6,9 @@ def do_import():
 
 
 if __name__ == '__main__':
+    import sys
+    import eventlet
+
     eventlet.monkey_patch()
     threading = eventlet.patcher.original('threading')
 
diff --git a/python-eventlet/tests/isolated/patcher_socketserver_selectors.py b/python-eventlet/tests/isolated/patcher_socketserver_selectors.py
new file mode 100644 (file)
index 0000000..91b56cb
--- /dev/null
@@ -0,0 +1,30 @@
+__test__ = False
+
+if __name__ == '__main__':
+    import eventlet
+    eventlet.monkey_patch()
+
+    from eventlet.support.six.moves.BaseHTTPServer import (
+        HTTPServer,
+        BaseHTTPRequestHandler,
+    )
+    import threading
+
+    server = HTTPServer(('localhost', 0), BaseHTTPRequestHandler)
+    thread = threading.Thread(target=server.serve_forever)
+
+    # Before fixing it the code would never go pass this line because:
+    # * socketserver.BaseServer that's used behind the scenes here uses
+    #   selectors.PollSelector if it's available and we don't have green poll
+    #   implementation so this just couldn't work
+    # * making socketserver use selectors.SelectSelector wasn't enough as
+    #   until now we just failed to monkey patch selectors module
+    #
+    # Due to the issues above this thread.start() call effectively behaved
+    # like calling server.serve_forever() directly in the current thread
+    #
+    # Original report: https://github.com/eventlet/eventlet/issues/249
+    thread.start()
+
+    server.shutdown()
+    print('pass')
index 5bce0f2a892ae2bd190b496fb4716af8a60fb249..63903312efbc83ac7bccb9d3025716961466ad57 100644 (file)
@@ -1,11 +1,8 @@
 # Issue #185: test threading.Condition with monkey-patching
-import eventlet
-
-# no standard tests in this file, ignore
 __test__ = False
 
-
 if __name__ == '__main__':
+    import eventlet
     eventlet.monkey_patch()
 
     import threading
index 4361f526d6df6f75ecf933df9cd7b3305978a0c9..2d428c40f02dca5af4a82aa4cf9b01642fcbb5b7 100644 (file)
@@ -1,11 +1,8 @@
 # Issue #223: test threading.Thread.join with monkey-patching
-import eventlet
-
-# no standard tests in this file, ignore
 __test__ = False
 
-
 if __name__ == '__main__':
+    import eventlet
     eventlet.monkey_patch()
 
     import threading
diff --git a/python-eventlet/tests/isolated/subprocess_patched_communicate.py b/python-eventlet/tests/isolated/subprocess_patched_communicate.py
new file mode 100644 (file)
index 0000000..1f17d89
--- /dev/null
@@ -0,0 +1,11 @@
+__test__ = False
+
+if __name__ == '__main__':
+    import sys
+    import eventlet
+    import subprocess
+    eventlet.monkey_patch(all=True)
+    p = subprocess.Popen([sys.executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    p.communicate()
+
+    print('pass')
index 6a8c6233d8992408d800bf1e3c7fdd5a671269b0..c2c7d3ce2880e856ff563d2afdea26843c4b8503 100644 (file)
@@ -42,6 +42,7 @@ class BufferLog(object):
     @staticmethod
     def write(s):
         output_buffer.append(s.rstrip())
+        return len(s)
 
 
 # This test might make you wince
diff --git a/python-eventlet/tests/openssl_test.py b/python-eventlet/tests/openssl_test.py
new file mode 100644 (file)
index 0000000..a4498dc
--- /dev/null
@@ -0,0 +1,17 @@
+import tests
+
+
+def test_import():
+    # https://github.com/eventlet/eventlet/issues/238
+    # Ensure that it's possible to import eventlet.green.OpenSSL.
+    # Most basic test to check Python 3 compatibility.
+    try:
+        import OpenSSL
+    except ImportError:
+        raise tests.SkipTest('need pyopenssl')
+
+    import eventlet.green.OpenSSL.SSL
+    import eventlet.green.OpenSSL.crypto
+    import eventlet.green.OpenSSL.rand
+    import eventlet.green.OpenSSL.tsafe
+    import eventlet.green.OpenSSL.version
index 2e458c557dff97cf73c412cf5d745df610b4f594..7ec12b12e67f30e83f85d9982eee721c99077630 100644 (file)
@@ -291,18 +291,20 @@ import time
         self.assertEqual(len(lines), 2, "\n".join(lines))
 
 
-class Subprocess(ProcessBase):
-    def test_monkeypatched_subprocess(self):
-        new_mod = """import eventlet
+def test_subprocess_after_monkey_patch():
+    code = '''\
+import sys
+import eventlet
 eventlet.monkey_patch()
 from eventlet.green import subprocess
-
-subprocess.Popen(['true'], stdin=subprocess.PIPE)
-print("done")
-"""
-        self.write_to_tempfile("newmod", new_mod)
-        output, lines = self.launch_subprocess('newmod')
-        self.assertEqual(output, "done\n", output)
+subprocess.Popen([sys.executable, '-c', ''], stdin=subprocess.PIPE).wait()
+print('pass')
+'''
+    output = tests.run_python(
+        path=None,
+        args=['-c', code],
+    )
+    assert output.rstrip() == b'pass'
 
 
 class Threading(ProcessBase):
@@ -324,8 +326,8 @@ print(len(_threading._active))
         output, lines = self.launch_subprocess('newmod')
         self.assertEqual(len(lines), 4, "\n".join(lines))
         assert lines[0].startswith('<Thread'), lines[0]
-        self.assertEqual(lines[1], "1", lines[1])
-        self.assertEqual(lines[2], "1", lines[2])
+        assert lines[1] == '1', lines
+        assert lines[2] == '1', lines
 
     def test_threading(self):
         new_mod = """import eventlet
@@ -506,3 +508,11 @@ def test_threading_condition():
 
 def test_threading_join():
     tests.run_isolated('patcher_threading_join.py')
+
+
+def test_socketserver_selectors():
+    tests.run_isolated('patcher_socketserver_selectors.py')
+
+
+def test_blocking_select_methods_are_deleted():
+    tests.run_isolated('patcher_blocking_select_methods_are_deleted.py')
index ced91364534fc7ee8424ff1a32b6f19369115dca..53a81f0f3fb9367d7d1f4b3f17af386a24ee8c87 100644 (file)
@@ -65,4 +65,4 @@ def test_semaphore_contention():
 
 
 if __name__ == '__main__':
-    unittest.main()
\ No newline at end of file
+    unittest.main()
index 60881f72b3074066d5222f302bb64e51f77986fb..634e1f0cc035b9da1d4af104bd5b870ee10bc719 100644 (file)
@@ -1,3 +1,4 @@
+import eventlet
 from eventlet.green import socket
 
 
@@ -6,3 +7,25 @@ def test_create_connection_error():
         socket.create_connection(('192.0.2.1', 80), timeout=0.1)
     except (IOError, OSError):
         pass
+
+
+def test_recv_type():
+    # https://github.com/eventlet/eventlet/issues/245
+    # socket recv returning multiple data types
+    # For this test to work, client and server have to be in separate
+    # processes or OS threads. Just running two greenthreads gives
+    # false test pass.
+    threading = eventlet.patcher.original('threading')
+    addr = []
+
+    def server():
+        sock = eventlet.listen(('127.0.0.1', 0))
+        addr[:] = sock.getsockname()
+        eventlet.sleep(0.2)
+
+    server_thread = threading.Thread(target=server)
+    server_thread.start()
+    eventlet.sleep(0.1)
+    sock = eventlet.connect(tuple(addr))
+    s = sock.recv(1)
+    assert isinstance(s, bytes)
index 085f656c1e8695cafa04c545a7e010f446b2bb40..1f07518332d0ab22a0cd1fb560c9fb84727211f2 100644 (file)
@@ -1,8 +1,10 @@
+import sys
+import time
+
 import eventlet
 from eventlet.green import subprocess
 import eventlet.patcher
-import sys
-import time
+import tests
 original_subprocess = eventlet.patcher.original('subprocess')
 
 
@@ -64,3 +66,20 @@ def test_close_popen_stdin_with_close_fds():
         p.stdin.close()
     except Exception as e:
         assert False, "Exception should not be raised, got %r instead" % e
+
+
+def test_universal_lines():
+    p = subprocess.Popen(
+        [sys.executable, '--version'],
+        shell=False,
+        stdout=subprocess.PIPE,
+        universal_newlines=True)
+    p.communicate(None)
+
+
+def test_patched_communicate_290():
+    # https://github.com/eventlet/eventlet/issues/290
+    # Certain order of import and monkey_patch breaks subprocess communicate()
+    # with AttributeError module `select` has no `poll` on Linux
+    # unpatched methods are removed for safety reasons in commit f63165c0e3
+    tests.run_isolated('subprocess_patched_communicate.py')
index caf8dbf8fd6128384d66b4754c49444d7b4f1dad..425ba14ef41bf6095332f0acd086c1b876086e2c 100644 (file)
@@ -8,7 +8,7 @@ from eventlet import greenthread
 from eventlet.green import thread
 from eventlet.support import six
 
-from tests import LimitedTestCase, skipped
+from tests import LimitedTestCase
 
 
 class Locals(LimitedTestCase):
@@ -24,29 +24,6 @@ class Locals(LimitedTestCase):
         self.results = []
         super(Locals, self).tearDown()
 
-    @skipped  # cause it relies on internal details of corolocal that are no longer true
-    def test_simple(self):
-        tls = thread._local()
-        g_ids = []
-        evt = event.Event()
-
-        def setter(tls, v):
-            g_id = id(greenthread.getcurrent())
-            g_ids.append(g_id)
-            tls.value = v
-            evt.wait()
-
-        thread.start_new_thread(setter, args=(tls, 1))
-        thread.start_new_thread(setter, args=(tls, 2))
-        eventlet.sleep()
-        objs = object.__getattribute__(tls, "__objs")
-        assert sorted(g_ids) == sorted(objs.keys())
-        assert objs[g_ids[0]]['value'] == 1
-        assert objs[g_ids[1]]['value'] == 2
-        assert getattr(tls, 'value', None) is None
-        evt.send("done")
-        eventlet.sleep()
-
     def test_assignment(self):
         my_local = corolocal.local()
         my_local.a = 1
index 818bb4561ea1f67b0086aa08f4422944fdc7ffcc..e1479de3347f930e1f820fbcfe0e1219bf9cbcb1 100644 (file)
@@ -22,7 +22,7 @@ import time
 import eventlet
 from eventlet import tpool, debug, event
 from eventlet.support import six
-from tests import LimitedTestCase, skipped, skip_with_pyevent, main
+from tests import LimitedTestCase, skip_with_pyevent, main
 
 
 one = 1
@@ -339,28 +339,6 @@ class TpoolLongTests(LimitedTestCase):
         self.assertEqual(len(results), cnt)
         tpool.killall()
 
-    @skipped
-    def test_benchmark(self):
-        """ Benchmark computing the amount of overhead tpool adds to function calls."""
-        iterations = 10000
-        import timeit
-        imports = """
-from tests.tpool_test import noop
-from eventlet.tpool import execute
-        """
-        t = timeit.Timer("noop()", imports)
-        results = t.repeat(repeat=3, number=iterations)
-        best_normal = min(results)
-
-        t = timeit.Timer("execute(noop)", imports)
-        results = t.repeat(repeat=3, number=iterations)
-        best_tpool = min(results)
-
-        tpool_overhead = (best_tpool - best_normal) / iterations
-        print("%s iterations\nTpool overhead is %s seconds per call.  Normal: %s; Tpool: %s" % (
-            iterations, tpool_overhead, best_normal, best_tpool))
-        tpool.killall()
-
     @skip_with_pyevent
     def test_leakage_from_tracebacks(self):
         tpool.execute(noop)  # get it started
index 381d51cb3d6969f60eb8ae9544935ec46cb05886..712bccdc4f75fc74c6f4aea33111e06dcf74f658 100644 (file)
@@ -8,7 +8,7 @@ from eventlet.green import httplib
 from eventlet.green import socket
 from eventlet.support import six
 
-from tests.wsgi_test import _TestBase
+import tests.wsgi_test
 
 
 # demo app
@@ -32,7 +32,7 @@ def handle(ws):
 wsapp = websocket.WebSocketWSGI(handle)
 
 
-class TestWebSocket(_TestBase):
+class TestWebSocket(tests.wsgi_test._TestBase):
     TEST_TIMEOUT = 5
 
     def set_site(self):
@@ -42,11 +42,11 @@ class TestWebSocket(_TestBase):
         headers = dict(kv.split(': ') for kv in [
             "Upgrade: websocket",
             # NOTE: intentionally no connection header
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Version: 13",
         ])
-        http = httplib.HTTPConnection('localhost', self.port)
+        http = httplib.HTTPConnection(*self.server_addr)
         http.request("GET", "/echo", headers=headers)
         resp = http.getresponse()
 
@@ -58,11 +58,11 @@ class TestWebSocket(_TestBase):
         headers = dict(kv.split(': ') for kv in [
             "Upgrade: websocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Version: 13",
         ])
-        http = httplib.HTTPConnection('localhost', self.port)
+        http = httplib.HTTPConnection(*self.server_addr)
         http.request("GET", "/echo", headers=headers)
         resp = http.getresponse()
 
@@ -73,11 +73,11 @@ class TestWebSocket(_TestBase):
         # No Upgrade now
         headers = dict(kv.split(': ') for kv in [
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Version: 13",
         ])
-        http = httplib.HTTPConnection('localhost', self.port)
+        http = httplib.HTTPConnection(*self.server_addr)
         http.request("GET", "/echo", headers=headers)
         resp = http.getresponse()
 
@@ -91,12 +91,12 @@ class TestWebSocket(_TestBase):
                 "GET /echo HTTP/1.1",
                 "Upgrade: websocket",
                 "Connection: %s" % http_connection,
-                "Host: localhost:%s" % self.port,
-                "Origin: http://localhost:%s" % self.port,
+                "Host: %s:%s" % self.server_addr,
+                "Origin: http://%s:%s" % self.server_addr,
                 "Sec-WebSocket-Version: 13",
                 "Sec-WebSocket-Key: d9MXuOzlVQ0h+qRllvSCIg==",
             ]
-            sock = eventlet.connect(('localhost', self.port))
+            sock = eventlet.connect(self.server_addr)
 
             sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
             result = sock.recv(1024)
@@ -114,14 +114,12 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: websocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Version: 13",
             "Sec-WebSocket-Key: d9MXuOzlVQ0h+qRllvSCIg==",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
-
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         sock.recv(1024)
         ws = websocket.RFC6455WebSocket(sock, {}, client=True)
@@ -154,13 +152,12 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: websocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Version: 13",
             "Sec-WebSocket-Key: d9MXuOzlVQ0h+qRllvSCIg==",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         sock.recv(1024)  # get the headers
         sock.close()  # close while the app is running
@@ -187,13 +184,12 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: websocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Version: 13",
             "Sec-WebSocket-Key: d9MXuOzlVQ0h+qRllvSCIg==",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         sock.recv(1024)  # get the headers
         closeframe = struct.pack('!BBIH', 1 << 7 | 8, 1 << 7 | 2, 0, 1000)
@@ -221,13 +217,12 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: websocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Version: 13",
             "Sec-WebSocket-Key: d9MXuOzlVQ0h+qRllvSCIg==",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         sock.recv(1024)  # get the headers
         sock.sendall(b'\x07\xff')  # Weird packet.
index 2858da00bd0f0a30e964c22e7f1cbf2b7c192a80..3ff500a7b2406dcfd99f9a89ccfdfd0f0ce1dca0 100644 (file)
@@ -8,9 +8,9 @@ from eventlet.green import httplib
 from eventlet.support import six
 from eventlet.websocket import WebSocket, WebSocketWSGI
 
-from tests import certificate_file, LimitedTestCase, mock, private_key_file
-from tests import skip_if_no_ssl
-from tests.wsgi_test import _TestBase
+import tests
+from tests import mock
+import tests.wsgi_test
 
 
 # demo app
@@ -34,14 +34,14 @@ def handle(ws):
 wsapp = WebSocketWSGI(handle)
 
 
-class TestWebSocket(_TestBase):
+class TestWebSocket(tests.wsgi_test._TestBase):
     TEST_TIMEOUT = 5
 
     def set_site(self):
         self.site = wsapp
 
     def test_incorrect_headers(self):
-        http = httplib.HTTPConnection('localhost', self.port)
+        http = httplib.HTTPConnection(*self.server_addr)
         http.request("GET", "/echo")
         response = http.getresponse()
         assert response.status == 400
@@ -50,11 +50,11 @@ class TestWebSocket(_TestBase):
         headers = dict(kv.split(': ') for kv in [
             "Upgrade: WebSocket",
             # NOTE: intentionally no connection header
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "WebSocket-Protocol: ws",
         ])
-        http = httplib.HTTPConnection('localhost', self.port)
+        http = httplib.HTTPConnection(*self.server_addr)
         http.request("GET", "/echo", headers=headers)
         resp = http.getresponse()
 
@@ -67,11 +67,11 @@ class TestWebSocket(_TestBase):
         headers = dict(kv.split(': ') for kv in [
             "Upgrade: WebSocket",
             # NOTE: intentionally no connection header
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
         ])
-        http = httplib.HTTPConnection('localhost', self.port)
+        http = httplib.HTTPConnection(*self.server_addr)
         http.request("GET", "/echo", headers=headers)
         resp = http.getresponse()
 
@@ -83,13 +83,13 @@ class TestWebSocket(_TestBase):
         headers = dict(kv.split(': ') for kv in [
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             # NOTE: Intentionally no Key2 header
         ])
-        http = httplib.HTTPConnection('localhost', self.port)
+        http = httplib.HTTPConnection(*self.server_addr)
         http.request("GET", "/echo", headers=headers)
         resp = http.getresponse()
 
@@ -102,12 +102,11 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "WebSocket-Protocol: ws",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         result = sock.recv(1024)
@@ -116,8 +115,8 @@ class TestWebSocket(_TestBase):
             'HTTP/1.1 101 Web Socket Protocol Handshake',
             'Upgrade: WebSocket',
             'Connection: Upgrade',
-            'WebSocket-Origin: http://localhost:%s' % self.port,
-            'WebSocket-Location: ws://localhost:%s/echo\r\n\r\n' % self.port,
+            'WebSocket-Origin: http://%s:%s' % self.server_addr,
+            'WebSocket-Location: ws://%s:%s/echo\r\n\r\n' % self.server_addr,
         ])))
 
     def test_correct_upgrade_request_76(self):
@@ -125,14 +124,13 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         result = sock.recv(1024)
@@ -141,9 +139,9 @@ class TestWebSocket(_TestBase):
             'HTTP/1.1 101 WebSocket Protocol Handshake',
             'Upgrade: WebSocket',
             'Connection: Upgrade',
-            'Sec-WebSocket-Origin: http://localhost:%s' % self.port,
+            'Sec-WebSocket-Origin: http://%s:%s' % self.server_addr,
             'Sec-WebSocket-Protocol: ws',
-            'Sec-WebSocket-Location: ws://localhost:%s/echo\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.port,
+            'Sec-WebSocket-Location: ws://%s:%s/echo\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.server_addr,
         ])))
 
     def test_query_string(self):
@@ -152,14 +150,13 @@ class TestWebSocket(_TestBase):
             "GET /echo?query_string HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         result = sock.recv(1024)
@@ -167,10 +164,10 @@ class TestWebSocket(_TestBase):
             'HTTP/1.1 101 WebSocket Protocol Handshake',
             'Upgrade: WebSocket',
             'Connection: Upgrade',
-            'Sec-WebSocket-Origin: http://localhost:%s' % self.port,
+            'Sec-WebSocket-Origin: http://%s:%s' % self.server_addr,
             'Sec-WebSocket-Protocol: ws',
             'Sec-WebSocket-Location: '
-            'ws://localhost:%s/echo?query_string\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.port,
+            'ws://%s:%s/echo?query_string\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.server_addr,
         ])))
 
     def test_empty_query_string(self):
@@ -179,14 +176,13 @@ class TestWebSocket(_TestBase):
             "GET /echo? HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         result = sock.recv(1024)
@@ -194,9 +190,9 @@ class TestWebSocket(_TestBase):
             'HTTP/1.1 101 WebSocket Protocol Handshake',
             'Upgrade: WebSocket',
             'Connection: Upgrade',
-            'Sec-WebSocket-Origin: http://localhost:%s' % self.port,
+            'Sec-WebSocket-Origin: http://%s:%s' % self.server_addr,
             'Sec-WebSocket-Protocol: ws',
-            'Sec-WebSocket-Location: ws://localhost:%s/echo?\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.port,
+            'Sec-WebSocket-Location: ws://%s:%s/echo?\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.server_addr,
         ])))
 
     def test_sending_messages_to_websocket_75(self):
@@ -204,12 +200,11 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "WebSocket-Protocol: ws",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         sock.recv(1024)
@@ -230,14 +225,13 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         sock.recv(1024)
@@ -258,12 +252,11 @@ class TestWebSocket(_TestBase):
             "GET /range HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "WebSocket-Protocol: ws",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         resp = sock.recv(1024)
@@ -281,14 +274,13 @@ class TestWebSocket(_TestBase):
             "GET /range HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         resp = sock.recv(1024)
@@ -321,12 +313,11 @@ class TestWebSocket(_TestBase):
             "GET /range HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "WebSocket-Protocol: ws",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         sock.recv(1024)  # get the headers
         sock.close()  # close while the app is running
@@ -353,14 +344,13 @@ class TestWebSocket(_TestBase):
             "GET /range HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         sock.recv(1024)  # get the headers
         sock.close()  # close while the app is running
@@ -387,14 +377,13 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         sock.recv(1024)  # get the headers
         sock.sendall(b'\xff\x00')  # "Close the connection" packet.
@@ -421,14 +410,13 @@ class TestWebSocket(_TestBase):
             "GET /echo HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         sock.recv(1024)  # get the headers
         sock.sendall(b'\xef\x00')  # Weird packet.
@@ -440,15 +428,13 @@ class TestWebSocket(_TestBase):
             "GET / HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
-
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         resp = sock.recv(1024)
         headers, result = resp.split(b'\r\n\r\n')
@@ -475,12 +461,11 @@ class TestWebSocket(_TestBase):
             "GET /error HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "WebSocket-Protocol: ws",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n'))
         sock.recv(1024)
         done_with_request.wait()
@@ -506,43 +491,41 @@ class TestWebSocket(_TestBase):
             "GET /error HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.connect(
-            ('localhost', self.port))
+        sock = eventlet.connect(self.server_addr)
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         sock.recv(1024)
         done_with_request.wait()
         assert error_detected[0]
 
 
-class TestWebSocketSSL(_TestBase):
+class TestWebSocketSSL(tests.wsgi_test._TestBase):
     def set_site(self):
         self.site = wsapp
 
-    @skip_if_no_ssl
+    @tests.skip_if_no_ssl
     def test_ssl_sending_messages(self):
         s = eventlet.wrap_ssl(eventlet.listen(('localhost', 0)),
-                              certfile=certificate_file,
-                              keyfile=private_key_file,
+                              certfile=tests.certificate_file,
+                              keyfile=tests.private_key_file,
                               server_side=True)
         self.spawn_server(sock=s)
         connect = [
             "GET /echo HTTP/1.1",
             "Upgrade: WebSocket",
             "Connection: Upgrade",
-            "Host: localhost:%s" % self.port,
-            "Origin: http://localhost:%s" % self.port,
+            "Host: %s:%s" % self.server_addr,
+            "Origin: http://%s:%s" % self.server_addr,
             "Sec-WebSocket-Protocol: ws",
             "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5",
             "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00",
         ]
-        sock = eventlet.wrap_ssl(eventlet.connect(
-            ('localhost', self.port)))
+        sock = eventlet.wrap_ssl(eventlet.connect(self.server_addr))
 
         sock.sendall(six.b('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U'))
         first_resp = b''
@@ -553,8 +536,9 @@ class TestWebSocketSSL(_TestBase):
         # make sure it sets the wss: protocol on the location header
         loc_line = [x for x in first_resp.split(b"\r\n")
                     if x.lower().startswith(b'sec-websocket-location')][0]
-        self.assert_(b"wss://localhost" in loc_line,
-                     "Expecting wss protocol in location: %s" % loc_line)
+        expect_wss = ('wss://%s:%s' % self.server_addr).encode()
+        assert expect_wss in loc_line, "Expecting wss protocol in location: %s" % loc_line
+
         sock.sendall(b'\x00hello\xFF')
         result = sock.recv(1024)
         self.assertEqual(result, b'\x00hello\xff')
@@ -568,7 +552,7 @@ class TestWebSocketSSL(_TestBase):
         eventlet.sleep(0.01)
 
 
-class TestWebSocketObject(LimitedTestCase):
+class TestWebSocketObject(tests.LimitedTestCase):
 
     def setUp(self):
         self.mock_socket = s = mock.Mock()
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)
 
index e4f4e769763c5462ebe4709090d8343c988fbe14..3094c71a229cdd77d593f074fa4105d09486fec6 100644 (file)
@@ -28,7 +28,7 @@ commands =
     pep8 benchmarks/ eventlet/ tests/
 
 [testenv]
-downloadcache = {toxworkdir}/pip_download_cache
+passenv = TRAVIS*
 setenv =
     PYTHONDONTWRITEBYTECODE = 1
     selects: EVENTLET_HUB = selects