Hello community,

here is the log from the commit of package python-ws4py for openSUSE:Leap:15.2 
checked in at 2020-02-25 12:18:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/python-ws4py (Old)
 and      /work/SRC/openSUSE:Leap:15.2/.python-ws4py.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-ws4py"

Tue Feb 25 12:18:26 2020 rev:11 rq:778886 version:0.5.1

Changes:
--------
--- /work/SRC/openSUSE:Leap:15.2/python-ws4py/python-ws4py.changes      
2020-01-15 15:54:15.367625855 +0100
+++ /work/SRC/openSUSE:Leap:15.2/.python-ws4py.new.26092/python-ws4py.changes   
2020-02-25 12:18:26.820589618 +0100
@@ -1,0 +2,21 @@
+Mon Feb 17 22:28:45 UTC 2020 - Julio González Gil <[email protected]>
+
+- Fix license. Correct one is BSD-3-Clause.
+
+-------------------------------------------------------------------
+Thu Apr 11 12:09:24 UTC 2019 - Marketa Calabkova <[email protected]>
+
+- update to version 0.5.1
+  * fixed runtime error: Set changed size during iteration
+  * on secure, only pass the requested number of bytes to the parsers
+  * Change threaded client test to test ssl socket
+  * exclude certain headers when requested
+  * Disable build for Python 3.4
+- launch tests using multibuild
+
+-------------------------------------------------------------------
+Tue Dec  4 12:56:13 UTC 2018 - Matej Cepl <[email protected]>
+
+- Remove superfluous devel dependency for noarch package
+
+-------------------------------------------------------------------

Old:
----
  ws4py-0.4.2.tar.gz

New:
----
  _multibuild
  ws4py-0.5.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-ws4py.spec ++++++
--- /var/tmp/diff_new_pack.sXdFtx/_old  2020-02-25 12:18:27.112590223 +0100
+++ /var/tmp/diff_new_pack.sXdFtx/_new  2020-02-25 12:18:27.112590223 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-ws4py
 #
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -12,31 +12,39 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
+#
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%global flavor @BUILD_FLAVOR@%{nil}
+%if "%{flavor}" == "test"
+%define psuffix -test
 %bcond_without  test
-Name:           python-ws4py
-Version:        0.4.2
+%else
+%define psuffix %{nil}
+%bcond_with test
+%endif
+Name:           python-ws4py%{psuffix}
+Version:        0.5.1
 Release:        0
-License:        BSD-2-Clause
 Summary:        WebSocket client and server library for Python
-Url:            https://github.com/Lawouach/WebSocket-for-Python
+License:        BSD-3-Clause
 Group:          Development/Languages/Python
+URL:            https://github.com/Lawouach/WebSocket-for-Python
 Source:         
https://files.pythonhosted.org/packages/source/w/ws4py/ws4py-%{version}.tar.gz
+BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
-BuildRequires:  %{python_module devel}
-BuildRequires:  %{python_module setuptools}
+BuildArch:      noarch
 %if %{with test}
-BuildRequires:  %{python_module CherryPy}
 BuildRequires:  %{python_module gevent}
 BuildRequires:  %{python_module greenlet}
+BuildRequires:  %{python_module mock}
+BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module tornado}
+BuildRequires:  %{python_module ws4py = %{version}}
 %endif
-BuildArch:      noarch
-
 %python_subpackages
 
 %description
@@ -45,21 +53,28 @@
 
 %prep
 %setup -q -n ws4py-%{version}
+# CherryPy is python3 only to ease the testing just skip it here
+rm test/test_cherrypy.py
 
 %build
 %python_build
 
 %install
+%if !%{with test}
 %python_install
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
+%endif
 
 %if %{with test}
 %check
-%python_exec setup.py test
+%pytest
 %endif
 
+%if !%{with test}
 %files %{python_files}
-%defattr(-,root,root,-)
+%license LICENSE
+%doc README.md
 %{python_sitelib}/*
+%endif
 
 %changelog

++++++ _multibuild ++++++
<multibuild>
  <package>test</package>
</multibuild>
++++++ ws4py-0.4.2.tar.gz -> ws4py-0.5.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/CHANGELOG.md new/ws4py-0.5.1/CHANGELOG.md
--- old/ws4py-0.4.2/CHANGELOG.md        1970-01-01 01:00:00.000000000 +0100
+++ new/ws4py-0.5.1/CHANGELOG.md        2018-02-28 17:24:13.000000000 +0100
@@ -0,0 +1,348 @@
+# Change Log
+
+## Unreleased
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/0.5.1...master)
+
+## [0.5.1](https://github.com/Lawouach/WebSocket-for-Python/tree/0.5.1) 
(2018-02-28)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/0.5.0...0.5.1)
+**Merged pull requests:**
+
+- Rudimentary fix and testcase for Issue #179 
[\#219](https://github.com/Lawouach/WebSocket-for-Python/pull/219) 
([medington](https://github.com/medington))
+
+## [0.5.0](https://github.com/Lawouach/WebSocket-for-Python/tree/0.5.0) 
(2018-02-27)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/0.4.3...0.5.0)
+**Merged pull requests:**
+
+- proper fix for #230: on secure, only pass the requested number of bytes to 
the parsers [\#239](https://github.com/Lawouach/WebSocket-for-Python/pull/239) 
([jmichiel](https://github.com/jmichiel))
+- fixed runtime error: Set changed size during iteration 
[\#233](https://github.com/Lawouach/WebSocket-for-Python/pull/233) 
([kamwoh](https://github.com/kamwoh))
+- Adds argument to set block value on gevent get command in 
WebSocketClient.receive() 
[\#221](https://github.com/Lawouach/WebSocket-for-Python/pull/221) 
([thaffenden](https://github.com/thaffenden))
+
+**Changes:**
+
+- Clarifies this project is on hiatus in README
+
+## [0.4.3](https://github.com/Lawouach/WebSocket-for-Python/tree/0.4.3) 
(2017-12-19)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/0.4.3...0.4.3)
+**Merged pull requests:**
+
+- Change threaded client test to test ssl socket 
[\#213](https://github.com/Lawouach/WebSocket-for-Python/pull/213) 
([awelkie](https://github.com/awelkie))
+- Create MANIFEST.in with LICENSE 
[\#215](https://github.com/Lawouach/WebSocket-for-Python/pull/215) 
([pmlandwehr](https://github.com/pmlandwehr))
+- exclude certain headers when requested 
[\#217](https://github.com/Lawouach/WebSocket-for-Python/pull/217) 
([klattimer](https://github.com/klattimer))
+- change from type() to isinstance() 
[\#236](https://github.com/Lawouach/WebSocket-for-Python/pull/236) 
([noam-graetz](https://github.com/noam-graetz))
+
+**Changes:**
+
+- Various test cleanups
+- Disable build for Python 3.4 as running into 
https://github.com/pypa/setuptools/issues/951 (Thinking of dropping official 
support for it too)
+
+## [0.4.2](https://github.com/Lawouach/WebSocket-for-Python/tree/0.4.2) 
(2017-03-29)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/0.4.1...0.4.2)
+
+**Merged pull requests:**
+
+- Block on receiving from SSL socket 
[\#212](https://github.com/Lawouach/WebSocket-for-Python/pull/212) 
([awelkie](https://github.com/awelkie))
+
+## [0.4.1](https://github.com/Lawouach/WebSocket-for-Python/tree/0.4.1) 
(2017-03-26)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/0.4.0...0.4.1)
+
+**Merged pull requests:**
+
+- fixes parsed hostname 
[\#210](https://github.com/Lawouach/WebSocket-for-Python/pull/210) 
([isubas](https://github.com/isubas))
+
+## [0.4.0](https://github.com/Lawouach/WebSocket-for-Python/tree/0.4.0) 
(2017-03-24)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/0.3.5...0.4.0)
+
+**Implemented enhancements:**
+
+- exception not catch in websocket.py always 
[\#70](https://github.com/Lawouach/WebSocket-for-Python/issues/70)
+
+**Fixed bugs:**
+
+- Last send never happens 
[\#167](https://github.com/Lawouach/WebSocket-for-Python/issues/167)
+
+**Closed issues:**
+
+- PyPI latest release 0.3.5 does not include \#205 and therefore breaks with 
cherrypy [\#209](https://github.com/Lawouach/WebSocket-for-Python/issues/209)
+- Unable to reconnect 
[\#207](https://github.com/Lawouach/WebSocket-for-Python/issues/207)
+- CherryPy does not use its own wsgiserver anymore, which ws4py depended on 
[\#205](https://github.com/Lawouach/WebSocket-for-Python/issues/205)
+- py2exe / python2.7 / syntax error in "yield from" lines 
[\#202](https://github.com/Lawouach/WebSocket-for-Python/issues/202)
+- Missing 0.3.5 changelog and tag 
[\#192](https://github.com/Lawouach/WebSocket-for-Python/issues/192)
+- ws4py 0.3.5 doesn't receive all messages over wss 
[\#191](https://github.com/Lawouach/WebSocket-for-Python/issues/191)
+- SSL: received\_message not getting called 
[\#183](https://github.com/Lawouach/WebSocket-for-Python/issues/183)
+- Python 2.6 support 
[\#182](https://github.com/Lawouach/WebSocket-for-Python/issues/182)
+- Overridden close\(\) not called under windows 
[\#178](https://github.com/Lawouach/WebSocket-for-Python/issues/178)
+- Enabling cpstats causes ws4py to crash 
[\#177](https://github.com/Lawouach/WebSocket-for-Python/issues/177)
+- Only support Python 3.0+ ? 
[\#175](https://github.com/Lawouach/WebSocket-for-Python/issues/175)
+- IOError\(interrupted system call\) on dropping privilages 
[\#172](https://github.com/Lawouach/WebSocket-for-Python/issues/172)
+- error: configure\_logger\(stdout=False, filepath="ws4py.log"\) 
[\#171](https://github.com/Lawouach/WebSocket-for-Python/issues/171)
+- Python 3.4 and gevent 1.1 
[\#170](https://github.com/Lawouach/WebSocket-for-Python/issues/170)
+- Is it possible to extract headers from WebSocketProtocol using asyncio 
[\#169](https://github.com/Lawouach/WebSocket-for-Python/issues/169)
+- server, opened\(\) is called each time a message is send 
[\#162](https://github.com/Lawouach/WebSocket-for-Python/issues/162)
+- tlm [\#160](https://github.com/Lawouach/WebSocket-for-Python/issues/160)
+- In opened\(\), closing connection would crash the server 
[\#159](https://github.com/Lawouach/WebSocket-for-Python/issues/159)
+- Server crashes with broken pipe if client disconnects ungracefully? 
[\#150](https://github.com/Lawouach/WebSocket-for-Python/issues/150)
+- Client connection hangs when using Cherrypy 
[\#146](https://github.com/Lawouach/WebSocket-for-Python/issues/146)
+- low priority: wsgiref example doesn't work 
[\#145](https://github.com/Lawouach/WebSocket-for-Python/issues/145)
+- CherryPy: simple example of an echo server 
[\#140](https://github.com/Lawouach/WebSocket-for-Python/issues/140)
+- WebSocketClient.closed\(\) always returns code 1006 if reason string empty. 
[\#137](https://github.com/Lawouach/WebSocket-for-Python/issues/137)
+- Server Side Connection Drops Immediately 
[\#134](https://github.com/Lawouach/WebSocket-for-Python/issues/134)
+- Calling WebSocketClient.terminate\(\) results in AttributeError 
[\#131](https://github.com/Lawouach/WebSocket-for-Python/issues/131)
+- ConnectionRefusedError: \[WinError 10061\] No connection could be made 
because the target machine actively refused it 
[\#130](https://github.com/Lawouach/WebSocket-for-Python/issues/130)
+- AttributeError: 'NoneType' object has no attribute 'fileno' 
[\#129](https://github.com/Lawouach/WebSocket-for-Python/issues/129)
+- wss is always one message behind 
[\#128](https://github.com/Lawouach/WebSocket-for-Python/issues/128)
+- Asyncio Issues 
[\#125](https://github.com/Lawouach/WebSocket-for-Python/issues/125)
+
+**Merged pull requests:**
+
+- change cherrypy.wsgiserver to cheroot.server 
[\#206](https://github.com/Lawouach/WebSocket-for-Python/pull/206) 
([raven38](https://github.com/raven38))
+- This change is to address the issue with run\_forever\(\) terminating too 
early. [\#201](https://github.com/Lawouach/WebSocket-for-Python/pull/201) 
([steowens](https://github.com/steowens))
+- Don't crash with broken pipe when trying to close a connection 
[\#198](https://github.com/Lawouach/WebSocket-for-Python/pull/198) 
([cristi8](https://github.com/cristi8))
+- adding heartbeat for gevent\_client 
[\#195](https://github.com/Lawouach/WebSocket-for-Python/pull/195) 
([alexmnt](https://github.com/alexmnt))
+- minor - typo 
[\#193](https://github.com/Lawouach/WebSocket-for-Python/pull/193) 
([johnwheeler](https://github.com/johnwheeler))
+- Eliminate a protocol error when first chunk is last too. 
[\#186](https://github.com/Lawouach/WebSocket-for-Python/pull/186) 
([plu9in](https://github.com/plu9in))
+- Allow WebSocketWSGIHandler to work even in presence of a middleware 
[\#185](https://github.com/Lawouach/WebSocket-for-Python/pull/185) 
([bozzzzo](https://github.com/bozzzzo))
+- Give application status code 1005 when no good status code is 
parsed/received 
[\#181](https://github.com/Lawouach/WebSocket-for-Python/pull/181) 
([isonmad](https://github.com/isonmad))
+- Fix server "Bad file descriptor" error under gevent 1.1, \#170 
[\#180](https://github.com/Lawouach/WebSocket-for-Python/pull/180) 
([hyt-hz](https://github.com/hyt-hz))
+- Version of example that doesn't need jquery, fix IOError on resume, f… 
[\#173](https://github.com/Lawouach/WebSocket-for-Python/pull/173) 
([EternityForest](https://github.com/EternityForest))
+- Fix typo [\#161](https://github.com/Lawouach/WebSocket-for-Python/pull/161) 
([hexchain](https://github.com/hexchain))
+- added a word 
[\#157](https://github.com/Lawouach/WebSocket-for-Python/pull/157) 
([Mrmaxmeier](https://github.com/Mrmaxmeier))
+- Removed unnecessary try/except and cleaned for some PEP8 
[\#155](https://github.com/Lawouach/WebSocket-for-Python/pull/155) 
([warvariuc](https://github.com/warvariuc))
+- Improve Origin handling in Client 
[\#154](https://github.com/Lawouach/WebSocket-for-Python/pull/154) 
([rdbhost](https://github.com/rdbhost))
+- Fix: closing handshake does not work correctly when reason is empty 
[\#149](https://github.com/Lawouach/WebSocket-for-Python/pull/149) 
([schiermike](https://github.com/schiermike))
+- pass ssl\_options to SSLIOStream\(\) to ensure certificate validation works 
[\#147](https://github.com/Lawouach/WebSocket-for-Python/pull/147) 
([szweep](https://github.com/szweep))
+- Explained why wsgiref not for produciton 
[\#144](https://github.com/Lawouach/WebSocket-for-Python/pull/144) 
([Seanny123](https://github.com/Seanny123))
+- Added Port to Host in handshake header. 
[\#139](https://github.com/Lawouach/WebSocket-for-Python/pull/139) 
([thiagorcdl](https://github.com/thiagorcdl))
+- Don't fail when websocket was not inited. 
[\#135](https://github.com/Lawouach/WebSocket-for-Python/pull/135) 
([eraviart](https://github.com/eraviart))
+- ws4py/\_\_init\_\_.py: fix configure\_logger by importing loging.handlers as 
handlers [\#133](https://github.com/Lawouach/WebSocket-for-Python/pull/133) 
([andrew-canaday](https://github.com/andrew-canaday))
+
+## [0.3.5](https://github.com/Lawouach/WebSocket-for-Python/tree/0.3.5) 
(2014-04-01)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.3.4...0.3.5)
+
+**Closed issues:**
+
+- No longer working on Chome 34.0.1847.76 
[\#124](https://github.com/Lawouach/WebSocket-for-Python/issues/124)
+
+## [v0.3.4](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.3.4) 
(2014-03-30)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.3.3...v0.3.4)
+
+**Fixed bugs:**
+
+- ws4py 0.3.3 installation broken 
[\#123](https://github.com/Lawouach/WebSocket-for-Python/issues/123)
+
+## [v0.3.3](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.3.3) 
(2014-03-29)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.3.2...v0.3.3)
+
+**Implemented enhancements:**
+
+- Upload releases to PyPI for compatibility with new pip versions 
[\#99](https://github.com/Lawouach/WebSocket-for-Python/issues/99)
+- Gradually drop support for Python \<2.7 and \<3.3.2 
[\#91](https://github.com/Lawouach/WebSocket-for-Python/issues/91)
+
+**Fixed bugs:**
+
+- Catch MemoryError when uploading a music file\(size: 16M\) 
[\#113](https://github.com/Lawouach/WebSocket-for-Python/issues/113)
+- ws4py 0.2.2 no longer working on Chrome version 30 
[\#108](https://github.com/Lawouach/WebSocket-for-Python/issues/108)
+
+**Closed issues:**
+
+- Exception in thread WebSocketClient during unit testing 
[\#122](https://github.com/Lawouach/WebSocket-for-Python/issues/122)
+- TypeError: \_\_str\_\_ returned non-string \(type bytes\) 
[\#121](https://github.com/Lawouach/WebSocket-for-Python/issues/121)
+- While using the Gevent WebSocket client an os error, OSError: \[Errno 24\] 
Too many open files, occurs 
[\#120](https://github.com/Lawouach/WebSocket-for-Python/issues/120)
+- AttributeError: 'MyClient' object has no attribute '\_cleanup' 
[\#119](https://github.com/Lawouach/WebSocket-for-Python/issues/119)
+- Message string representation does not work in Python 3 
[\#117](https://github.com/Lawouach/WebSocket-for-Python/issues/117)
+- Not able to install a specific version of an unsecure package 
[\#115](https://github.com/Lawouach/WebSocket-for-Python/issues/115)
+- How to use WebSocketManager with server? 
[\#111](https://github.com/Lawouach/WebSocket-for-Python/issues/111)
+- client: KeyboardInterrupt silently catched 
[\#109](https://github.com/Lawouach/WebSocket-for-Python/issues/109)
+- unittests2 shouldn't be needed with python \>= 2.7 and \>= 3.2 
[\#106](https://github.com/Lawouach/WebSocket-for-Python/issues/106)
+
+**Merged pull requests:**
+
+- base example documentation fix 
[\#118](https://github.com/Lawouach/WebSocket-for-Python/pull/118) 
([husio](https://github.com/husio))
+- fix: changed gevent server to use a real gevent.pool.Pool 
[\#114](https://github.com/Lawouach/WebSocket-for-Python/pull/114) 
([fischerq](https://github.com/fischerq))
+- Tutorial should import TornadoWebSocketClient. 
[\#112](https://github.com/Lawouach/WebSocket-for-Python/pull/112) 
([ajdavis](https://github.com/ajdavis))
+- Fix cherrypy logging 
[\#107](https://github.com/Lawouach/WebSocket-for-Python/pull/107) 
([UncleRus](https://github.com/UncleRus))
+
+## [v0.3.2](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.3.2) 
(2013-09-12)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.3.0-beta...v0.3.2)
+
+**Implemented enhancements:**
+
+- Move back to unicode and byte litterals 
[\#100](https://github.com/Lawouach/WebSocket-for-Python/issues/100)
+- remove implicit gevent monkey patching in the gevent client 
[\#90](https://github.com/Lawouach/WebSocket-for-Python/issues/90)
+- Busy loop in SelectPoller that is consuming a lot of CPU 
[\#87](https://github.com/Lawouach/WebSocket-for-Python/issues/87)
+- tornado expects bytes but TornadoWebSocketClient gives strings 
[\#71](https://github.com/Lawouach/WebSocket-for-Python/issues/71)
+
+**Fixed bugs:**
+
+- AssertionError: Header values must be strings 
[\#103](https://github.com/Lawouach/WebSocket-for-Python/issues/103)
+- remove implicit gevent monkey patching in the gevent client 
[\#90](https://github.com/Lawouach/WebSocket-for-Python/issues/90)
+- ws4py.server.wsgiutils.py got some error in python 3.3 
[\#88](https://github.com/Lawouach/WebSocket-for-Python/issues/88)
+- Busy loop in SelectPoller that is consuming a lot of CPU 
[\#87](https://github.com/Lawouach/WebSocket-for-Python/issues/87)
+- Socket not properly closed in Win7 
[\#69](https://github.com/Lawouach/WebSocket-for-Python/issues/69)
+
+**Closed issues:**
+
+- NameError: global name 'dec' is not defined \(in Python 2.7.5\) 
[\#102](https://github.com/Lawouach/WebSocket-for-Python/issues/102)
+- Allow cherrypy users to pass in a custom poller to the manager 
[\#95](https://github.com/Lawouach/WebSocket-for-Python/issues/95)
+- IPv6 sockets not supported 
[\#86](https://github.com/Lawouach/WebSocket-for-Python/issues/86)
+- Support `ws+unix` scheme 
[\#76](https://github.com/Lawouach/WebSocket-for-Python/issues/76)
+- Strange traceback with WebSocket.send 
[\#73](https://github.com/Lawouach/WebSocket-for-Python/issues/73)
+
+**Merged pull requests:**
+
+- fixed some old references to removed functions enc\(\) and dec\(\) 
[\#101](https://github.com/Lawouach/WebSocket-for-Python/pull/101) 
([flaviogrossi](https://github.com/flaviogrossi))
+- ws4py.client classes should support client certificates 
[\#98](https://github.com/Lawouach/WebSocket-for-Python/pull/98) 
([EliAndrewC](https://github.com/EliAndrewC))
+- Code correction in built-in client tutorial 
[\#96](https://github.com/Lawouach/WebSocket-for-Python/pull/96) 
([elmiko](https://github.com/elmiko))
+- Fixed a couple of typos in the docs 
[\#92](https://github.com/Lawouach/WebSocket-for-Python/pull/92) 
([rakiru](https://github.com/rakiru))
+- Fix typo in 'ws4y.websocket' 
[\#89](https://github.com/Lawouach/WebSocket-for-Python/pull/89) 
([jodal](https://github.com/jodal))
+- Fix for bytestrings in Tornado client. 
[\#85](https://github.com/Lawouach/WebSocket-for-Python/pull/85) 
([lbolla](https://github.com/lbolla))
+
+## 
[v0.3.0-beta](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.3.0-beta)
 (2013-03-16)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.2.4...v0.3.0-beta)
+
+**Closed issues:**
+
+- Threaded WebSocket client always exits randomly 
[\#78](https://github.com/Lawouach/WebSocket-for-Python/issues/78)
+- test\_cherrypy.py fails with py3 
[\#72](https://github.com/Lawouach/WebSocket-for-Python/issues/72)
+- A simpler gevent server example? 
[\#66](https://github.com/Lawouach/WebSocket-for-Python/issues/66)
+
+**Merged pull requests:**
+
+- select.select\(\) on Windows does not allow empty rlist. 
[\#83](https://github.com/Lawouach/WebSocket-for-Python/pull/83) 
([Who8MyLunch](https://github.com/Who8MyLunch))
+- DOC: add omitted default value for closed\(\) in Client example 
[\#80](https://github.com/Lawouach/WebSocket-for-Python/pull/80) 
([y-p](https://github.com/y-p))
+- README: Mention to wsaccel 
[\#79](https://github.com/Lawouach/WebSocket-for-Python/pull/79) 
([methane](https://github.com/methane))
+- Faster utf8validate. 
[\#75](https://github.com/Lawouach/WebSocket-for-Python/pull/75) 
([methane](https://github.com/methane))
+- Cleanup indent and trailing spaces. 
[\#74](https://github.com/Lawouach/WebSocket-for-Python/pull/74) 
([methane](https://github.com/methane))
+
+## [v0.2.4](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.2.4) 
(2012-12-13)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.2.3...v0.2.4)
+
+**Closed issues:**
+
+- echo\_cherrypy\_server with Python 3.2 fails on client connection 
[\#62](https://github.com/Lawouach/WebSocket-for-Python/issues/62)
+
+**Merged pull requests:**
+
+- Doc build improvements 
[\#65](https://github.com/Lawouach/WebSocket-for-Python/pull/65) 
([jodal](https://github.com/jodal))
+- Don't broadcast messages to terminated WebSockets 
[\#64](https://github.com/Lawouach/WebSocket-for-Python/pull/64) 
([jodal](https://github.com/jodal))
+- Fixup/readme 
[\#63](https://github.com/Lawouach/WebSocket-for-Python/pull/63) 
([richo](https://github.com/richo))
+
+## [v0.2.3](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.2.3) 
(2012-10-27)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.2.2...v0.2.3)
+
+**Closed issues:**
+
+- urlsplit in Python 2.6.1 and earlier doesn't parse ws or wss properly. 
[\#59](https://github.com/Lawouach/WebSocket-for-Python/issues/59)
+- Inconsistent tabs/spaces 
[\#58](https://github.com/Lawouach/WebSocket-for-Python/issues/58)
+- Bug in documentation 
[\#55](https://github.com/Lawouach/WebSocket-for-Python/issues/55)
+- wss server support 
[\#40](https://github.com/Lawouach/WebSocket-for-Python/issues/40)
+- Port to Python 3 
[\#29](https://github.com/Lawouach/WebSocket-for-Python/issues/29)
+
+**Merged pull requests:**
+
+- Work around Python 2.6.X bug in urlparse.urlsplit\(\) 
[\#60](https://github.com/Lawouach/WebSocket-for-Python/pull/60) 
([dsully](https://github.com/dsully))
+- Bug fix in WebSocketPlugin.broadcast\(\) 
[\#54](https://github.com/Lawouach/WebSocket-for-Python/pull/54) 
([ralhei](https://github.com/ralhei))
+- Minor fix which turns an unintentional and confusing error message into the 
intended error message 
[\#53](https://github.com/Lawouach/WebSocket-for-Python/pull/53) 
([EliAndrewC](https://github.com/EliAndrewC))
+
+## [v0.2.2](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.2.2) 
(2012-06-21)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.2.1...v0.2.2)
+
+**Closed issues:**
+
+- memory leak in streaming.Stream 
[\#51](https://github.com/Lawouach/WebSocket-for-Python/issues/51)
+- ws4py shouldn't send a masked message to the client 
[\#50](https://github.com/Lawouach/WebSocket-for-Python/issues/50)
+- In client/\_\_init\_\_.py: remaining 'body' bytes ignored 
[\#46](https://github.com/Lawouach/WebSocket-for-Python/issues/46)
+- ws4py.client.threadedclient is not compatible with 
ws4py.server.cherrypyserver 
[\#44](https://github.com/Lawouach/WebSocket-for-Python/issues/44)
+- infinite loop in threadedclient.py when server closes websocket 
[\#23](https://github.com/Lawouach/WebSocket-for-Python/issues/23)
+
+**Merged pull requests:**
+
+- Change Sec-WebSocket-Origin header to Origin as per RFC 
[\#49](https://github.com/Lawouach/WebSocket-for-Python/pull/49) 
([jtakkala](https://github.com/jtakkala))
+- Testing: Add support for `python setup.py test`, tox, and Travis CI 
[\#47](https://github.com/Lawouach/WebSocket-for-Python/pull/47) 
([msabramo](https://github.com/msabramo))
+- Fixed geventclient.WebSocketClient.receive\(\) blocking forever 
[\#45](https://github.com/Lawouach/WebSocket-for-Python/pull/45) 
([aluzzardi](https://github.com/aluzzardi))
+- Doctest fixed at ws4py.framing in Frame 
[\#43](https://github.com/Lawouach/WebSocket-for-Python/pull/43) 
([stuntgoat](https://github.com/stuntgoat))
+- Delete greenlet start\(\) 
[\#42](https://github.com/Lawouach/WebSocket-for-Python/pull/42) 
([yrttyr](https://github.com/yrttyr))
+
+## [v0.2.1](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.2.1) 
(2012-03-28)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.2.0...v0.2.1)
+
+**Closed issues:**
+
+- wsgi.input \_sock not working 
[\#41](https://github.com/Lawouach/WebSocket-for-Python/issues/41)
+- ImportError: No module named gevent 
[\#39](https://github.com/Lawouach/WebSocket-for-Python/issues/39)
+- HandshakeError: WebSocket version required 
[\#36](https://github.com/Lawouach/WebSocket-for-Python/issues/36)
+- tools.websocket.version should allow more than one version 
[\#34](https://github.com/Lawouach/WebSocket-for-Python/issues/34)
+- TornadoWebSocketClient doesn't support SSL 
[\#32](https://github.com/Lawouach/WebSocket-for-Python/issues/32)
+- Problem creating a websocket 
[\#31](https://github.com/Lawouach/WebSocket-for-Python/issues/31)
+- KeyboardInterrupt ignored in WebSocketClient 
[\#30](https://github.com/Lawouach/WebSocket-for-Python/issues/30)
+
+**Merged pull requests:**
+
+- Delete start\(\) 
[\#38](https://github.com/Lawouach/WebSocket-for-Python/pull/38) 
([yrttyr](https://github.com/yrttyr))
+- Tornado implementation fix 
[\#37](https://github.com/Lawouach/WebSocket-for-Python/pull/37) 
([protoss-player](https://github.com/protoss-player))
+- Fix SSL support in TornadoWebSocketClient and add missing import 
[\#33](https://github.com/Lawouach/WebSocket-for-Python/pull/33) 
([patrickod](https://github.com/patrickod))
+- Fix SSL Clients 
[\#27](https://github.com/Lawouach/WebSocket-for-Python/pull/27) 
([chadselph](https://github.com/chadselph))
+- send\(\) really shouldn't fail silently when getting an unknown data type 
[\#26](https://github.com/Lawouach/WebSocket-for-Python/pull/26) 
([chadselph](https://github.com/chadselph))
+
+## [v0.2.0](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.2.0) 
(2012-02-23)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.1.5...v0.2.0)
+
+**Closed issues:**
+
+- Incorrect args in TornadoWebSocketClient constructor 
[\#25](https://github.com/Lawouach/WebSocket-for-Python/issues/25)
+- Typo in echo\_client example 
[\#24](https://github.com/Lawouach/WebSocket-for-Python/issues/24)
+- Typos following recent code changes... 
[\#22](https://github.com/Lawouach/WebSocket-for-Python/issues/22)
+- ThreadedHandler still referenced 
[\#21](https://github.com/Lawouach/WebSocket-for-Python/issues/21)
+- memory leak 
[\#20](https://github.com/Lawouach/WebSocket-for-Python/issues/20)
+- Please store the close code and reason in WebSocketBaseClient 
[\#19](https://github.com/Lawouach/WebSocket-for-Python/issues/19)
+- Exception when running with gevent 1.0 
[\#18](https://github.com/Lawouach/WebSocket-for-Python/issues/18)
+- Version 0.1.5 is not installable 
[\#17](https://github.com/Lawouach/WebSocket-for-Python/issues/17)
+- TypeError: list indices must be integers, not str 
[\#16](https://github.com/Lawouach/WebSocket-for-Python/issues/16)
+- All frames from the client should be masked 
[\#15](https://github.com/Lawouach/WebSocket-for-Python/issues/15)
+- chrome 16.0.912.41 beta return code 400 
[\#11](https://github.com/Lawouach/WebSocket-for-Python/issues/11)
+
+## [v0.1.5](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.1.5) 
(2011-12-15)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.1.4...v0.1.5)
+
+**Closed issues:**
+
+- Bad code for binary send in client/\_\_init\_\_.py ?? 
[\#14](https://github.com/Lawouach/WebSocket-for-Python/issues/14)
+- Server not handling "Upgrade" header case-insensitively, as it should 
[\#13](https://github.com/Lawouach/WebSocket-for-Python/issues/13)
+
+**Merged pull requests:**
+
+- support for wss:// connections using ssl.wrap\_socket 
[\#12](https://github.com/Lawouach/WebSocket-for-Python/pull/12) 
([EliAndrewC](https://github.com/EliAndrewC))
+
+## [v0.1.4](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.1.4) 
(2011-11-12)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.1.3...v0.1.4)
+
+**Closed issues:**
+
+- Handle Connection: keep-alive, Upgrade header from Firefox 7.0 
[\#3](https://github.com/Lawouach/WebSocket-for-Python/issues/3)
+
+**Merged pull requests:**
+
+- Hello, I had to make some minor changes to get the echo server to work for 
me. Thank for the excellent work. I am looking forward to incorporating it into 
my site :\). Regards, Derrick 
[\#10](https://github.com/Lawouach/WebSocket-for-Python/pull/10) 
([dpetzold](https://github.com/dpetzold))
+- Added io\_loop parameter to TornadoWebSocketClient constructor 
[\#9](https://github.com/Lawouach/WebSocket-for-Python/pull/9) 
([swax](https://github.com/swax))
+- Websocket threading client ignores initial data send by the server 
[\#7](https://github.com/Lawouach/WebSocket-for-Python/pull/7) 
([majek](https://github.com/majek))
+- Fixed hangup in Tornado implementation 
[\#6](https://github.com/Lawouach/WebSocket-for-Python/pull/6) 
([protoss-player](https://github.com/protoss-player))
+- Added a gevent client and fixed a major flaw in the gevent/"wsgi" server 
handler [\#5](https://github.com/Lawouach/WebSocket-for-Python/pull/5) 
([progrium](https://github.com/progrium))
+- Some JSON functionality 
[\#4](https://github.com/Lawouach/WebSocket-for-Python/pull/4) 
([protoss-player](https://github.com/protoss-player))
+
+## [v0.1.3](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.1.3) 
(2011-09-07)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.1.2...v0.1.3)
+
+**Merged pull requests:**
+
+- Made it more generic, fixed some bugs, cleaned things up 
[\#2](https://github.com/Lawouach/WebSocket-for-Python/pull/2) 
([progrium](https://github.com/progrium))
+
+## [v0.1.2](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.1.2) 
(2011-08-23)
+[Full 
Changelog](https://github.com/Lawouach/WebSocket-for-Python/compare/v0.1.1...v0.1.2)
+
+**Merged pull requests:**
+
+- gevent server implementation 
[\#1](https://github.com/Lawouach/WebSocket-for-Python/pull/1) 
([progrium](https://github.com/progrium))
+
+## [v0.1.1](https://github.com/Lawouach/WebSocket-for-Python/tree/v0.1.1) 
(2011-08-21)
+
+
+\* *This Change Log was automatically generated by 
[github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/LICENSE new/ws4py-0.5.1/LICENSE
--- old/ws4py-0.4.2/LICENSE     1970-01-01 01:00:00.000000000 +0100
+++ new/ws4py-0.5.1/LICENSE     2017-05-09 12:35:59.000000000 +0200
@@ -0,0 +1,26 @@
+Copyright (c) 2011-2016, Sylvain Hellegouarch 
+All rights reserved. 
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met: 
+
+ * Redistributions of source code must retain the above copyright notice, 
+   this list of conditions and the following disclaimer. 
+ * Redistributions in binary form must reproduce the above copyright 
+   notice, this list of conditions and the following disclaimer in the 
+   documentation and/or other materials provided with the distribution. 
+ * Neither the name of ws4py nor the names of its contributors may be used 
+   to endorse or promote products derived from this software without 
+   specific prior written permission. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/MANIFEST.in new/ws4py-0.5.1/MANIFEST.in
--- old/ws4py-0.4.2/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100
+++ new/ws4py-0.5.1/MANIFEST.in 2017-10-17 12:52:58.000000000 +0200
@@ -0,0 +1,3 @@
+include LICENSE
+include README.md
+include CHANGELOG.md
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/PKG-INFO new/ws4py-0.5.1/PKG-INFO
--- old/ws4py-0.4.2/PKG-INFO    2017-03-29 19:57:06.000000000 +0200
+++ new/ws4py-0.5.1/PKG-INFO    2018-02-28 18:40:21.000000000 +0100
@@ -1,12 +1,13 @@
 Metadata-Version: 1.1
 Name: ws4py
-Version: 0.4.2
+Version: 0.5.1
 Summary: WebSocket client and server library for Python 2 and 3 as well as PyPy
 Home-page: https://github.com/Lawouach/WebSocket-for-Python
 Author: Sylvain Hellegouarch
 Author-email: [email protected]
 License: BSD
 Download-URL: https://pypi.python.org/pypi/ws4py
+Description-Content-Type: UNKNOWN
 Description: WebSocket client and server library for Python 2 and 3 as well as 
PyPy
 Platform: any
 Classifier: Development Status :: 5 - Production/Stable
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/README.md new/ws4py-0.5.1/README.md
--- old/ws4py-0.4.2/README.md   1970-01-01 01:00:00.000000000 +0100
+++ new/ws4py-0.5.1/README.md   2018-02-27 11:04:53.000000000 +0100
@@ -0,0 +1,24 @@
+# WebSocket for Python (ws4py)
+
+[![Gitter](https://badges.gitter.im/Lawouach/WebSocket-for-Python.svg)](https://gitter.im/Lawouach/WebSocket-for-Python?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+[![Build 
Status](https://travis-ci.org/Lawouach/WebSocket-for-Python.svg?branch=master)](https://travis-ci.org/Lawouach/WebSocket-for-Python)
+[![PyPI](https://img.shields.io/pypi/v/ws4py.svg)](https://pypi.org/project/ws4py/)
+
+Python library providing an implementation of the WebSocket protocol defined 
in [RFC 6455](http://tools.ietf.org/html/rfc6455).
+
+Read the [documentation](https://ws4py.readthedocs.org/en/latest/) for more 
information.
+
+You can also join the [ws4py 
mailing-list](http://groups.google.com/group/ws4py) to discuss the library.
+
+**WARNING**: This project is [on 
hiatus](https://opensource.guide/best-practices/#share-the-workload)
+and [does not receive active 
maintainance](http://www.defuze.org/archives/409-ws4py-is-eager-for-a-new-maintainer.html).
+Please be aware of this when deciding to rely on it as contributions may be 
slow
+to make their way to a new release. If you feel like offering help in
+maintaining it, [please let us 
know](https://groups.google.com/forum/#!forum/ws4py).
+
+## Installation
+
+```
+pip install ws4py
+```
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/setup.cfg new/ws4py-0.5.1/setup.cfg
--- old/ws4py-0.4.2/setup.cfg   2017-03-29 19:57:06.000000000 +0200
+++ new/ws4py-0.5.1/setup.cfg   2018-02-28 18:40:21.000000000 +0100
@@ -1,5 +1,4 @@
 [egg_info]
 tag_build = 
 tag_date = 0
-tag_svn_revision = 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/test/test_client.py 
new/ws4py-0.5.1/test/test_client.py
--- old/ws4py-0.4.2/test/test_client.py 2017-03-24 16:25:29.000000000 +0100
+++ new/ws4py-0.5.1/test/test_client.py 2018-02-28 17:21:12.000000000 +0100
@@ -1,32 +1,28 @@
 # -*- coding: utf-8 -*-
 from base64 import b64encode
 from hashlib import sha1
-import os
 import socket
 import time
 import unittest
 
-from mock import MagicMock, call, patch
+from mock import MagicMock, patch
 
-from ws4py.manager import WebSocketManager
-from ws4py.websocket import WebSocket
 from ws4py import WS_KEY
 from ws4py.exc import HandshakeError
 from ws4py.framing import Frame, OPCODE_TEXT, OPCODE_CLOSE
-from ws4py.messaging import CloseControlMessage
 from ws4py.client import WebSocketBaseClient
 from ws4py.client.threadedclient import WebSocketClient
 
 class BasicClientTest(unittest.TestCase):
     def test_invalid_hostname_in_url(self):
         self.assertRaises(ValueError, WebSocketBaseClient, 
url="qsdfqsd65qsd354")
-        
+
     def test_invalid_scheme_in_url(self):
         self.assertRaises(ValueError, WebSocketBaseClient, 
url="ftp://localhost";)
-    
+
     def test_invalid_hostname_in_url(self):
         self.assertRaises(ValueError, WebSocketBaseClient, url="ftp://?/";)
-    
+
     def test_parse_unix_schemes(self):
         c = WebSocketBaseClient(url="ws+unix:///my.socket")
         self.assertEqual(c.scheme, "ws+unix")
@@ -35,7 +31,7 @@
         self.assertEqual(c.unix_socket_path, "/my.socket")
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, "/my.socket")
-        
+
         c = WebSocketBaseClient(url="wss+unix:///my.socket")
         self.assertEqual(c.scheme, "wss+unix")
         self.assertEqual(c.host, "localhost")
@@ -43,7 +39,7 @@
         self.assertEqual(c.unix_socket_path, "/my.socket")
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, "/my.socket")
-        
+
     def test_parse_ws_scheme(self):
         c = WebSocketBaseClient(url="ws://127.0.0.1/")
         self.assertEqual(c.scheme, "ws")
@@ -51,7 +47,7 @@
         self.assertEqual(c.port, 80)
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 80))
-        
+
     def test_parse_ws_scheme_when_missing_resource(self):
         c = WebSocketBaseClient(url="ws://127.0.0.1")
         self.assertEqual(c.scheme, "ws")
@@ -59,7 +55,7 @@
         self.assertEqual(c.port, 80)
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 80))
-        
+
     def test_parse_ws_scheme_with_port(self):
         c = WebSocketBaseClient(url="ws://127.0.0.1:9090")
         self.assertEqual(c.scheme, "ws")
@@ -67,7 +63,7 @@
         self.assertEqual(c.port, 9090)
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 9090))
-        
+
     def test_parse_ws_scheme_with_query_string(self):
         c = WebSocketBaseClient(url="ws://127.0.0.1/?token=value")
         self.assertEqual(c.scheme, "ws")
@@ -75,7 +71,7 @@
         self.assertEqual(c.port, 80)
         self.assertEqual(c.resource, "/?token=value")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 80))
-        
+
     def test_parse_wss_scheme(self):
         c = WebSocketBaseClient(url="wss://127.0.0.1/")
         self.assertEqual(c.scheme, "wss")
@@ -83,7 +79,7 @@
         self.assertEqual(c.port, 443)
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 443))
-        
+
     def test_parse_wss_scheme_when_missing_resource(self):
         c = WebSocketBaseClient(url="wss://127.0.0.1")
         self.assertEqual(c.scheme, "wss")
@@ -91,7 +87,7 @@
         self.assertEqual(c.port, 443)
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 443))
-        
+
     def test_parse_wss_scheme_with_port(self):
         c = WebSocketBaseClient(url="wss://127.0.0.1:9090")
         self.assertEqual(c.scheme, "wss")
@@ -99,7 +95,7 @@
         self.assertEqual(c.port, 9090)
         self.assertEqual(c.resource, "/")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 9090))
-        
+
     def test_parse_wss_scheme_with_query_string(self):
         c = WebSocketBaseClient(url="wss://127.0.0.1/?token=value")
         self.assertEqual(c.scheme, "wss")
@@ -107,23 +103,26 @@
         self.assertEqual(c.port, 443)
         self.assertEqual(c.resource, "/?token=value")
         self.assertEqual(c.bind_addr, ("127.0.0.1", 443))
-        
+
     @patch('ws4py.client.socket')
     def test_connect_and_close(self, sock):
-        
+
         s = MagicMock()
         sock.socket.return_value = s
         sock.getaddrinfo.return_value = [(socket.AF_INET, socket.SOCK_STREAM, 
0, "",
                                           ("127.0.0.1", 80, 0, 0))]
 
         c = WebSocketBaseClient(url="ws://127.0.0.1/?token=value")
-        
+
         s.recv.return_value = b"\r\n".join([
             b"HTTP/1.1 101 Switching Protocols",
             b"Connection: Upgrade",
             b"Sec-Websocket-Version: 13",
             b"Content-Type: text/plain;charset=utf-8",
             b"Sec-Websocket-Accept: " + b64encode(sha1(c.key + 
WS_KEY).digest()),
+            b"Sec-WebSocket-Protocol: proto1, proto2",
+            b"Sec-WebSocket-Extensions: ext1, ext2",
+            b"Sec-WebSocket-Extensions: ext3",
             b"Upgrade: websocket",
             b"Date: Sun, 26 Jul 2015 12:32:55 GMT",
             b"Server: ws4py/test",
@@ -132,6 +131,8 @@
 
         c.connect()
         s.connect.assert_called_once_with(("127.0.0.1", 80))
+        self.assertEqual(c.protocols, [b'proto1', b'proto2'])
+        self.assertEqual(c.extensions, [b'ext1', b'ext2', b'ext3'])
 
         s.reset_mock()
         c.close(code=1006, reason="boom")
@@ -140,32 +141,32 @@
         f.parser.send(args[0][0])
         f.parser.close()
         self.assertIn(b'boom', f.unmask(f.body))
-        
+
     @patch('ws4py.client.socket')
     def test_empty_response(self, sock):
-        
+
         s = MagicMock()
         sock.socket.return_value = s
         sock.getaddrinfo.return_value = [(socket.AF_INET, socket.SOCK_STREAM, 
0, "",
                                           ("127.0.0.1", 80, 0, 0))]
 
         c = WebSocketBaseClient(url="ws://127.0.0.1/?token=value")
-        
+
         s.recv.return_value = b""
         self.assertRaises(HandshakeError, c.connect)
         s.shutdown.assert_called_once_with(socket.SHUT_RDWR)
         s.close.assert_called_once_with()
-        
+
     @patch('ws4py.client.socket')
-    def test_invdalid_response_code(self, sock):
-        
+    def test_invalid_response_code(self, sock):
+
         s = MagicMock()
         sock.socket.return_value = s
         sock.getaddrinfo.return_value = [(socket.AF_INET, socket.SOCK_STREAM, 
0, "",
                                           ("127.0.0.1", 80, 0, 0))]
 
         c = WebSocketBaseClient(url="ws://127.0.0.1/?token=value")
-        
+
         s.recv.return_value = b"\r\n".join([
             b"HTTP/1.1 200 Switching Protocols",
             b"Connection: Upgrade",
@@ -181,10 +182,10 @@
         self.assertRaises(HandshakeError, c.connect)
         s.shutdown.assert_called_once_with(socket.SHUT_RDWR)
         s.close.assert_called_once_with()
-        
+
     @patch('ws4py.client.socket')
     def test_invalid_response_headers(self, sock):
-        
+
         for key_header, invalid_value in ((b'upgrade', b'boom'),
                                           (b'connection', b'bim')):
             s = MagicMock()
@@ -192,7 +193,7 @@
             sock.getaddrinfo.return_value = [(socket.AF_INET, 
socket.SOCK_STREAM, 0, "",
                                               ("127.0.0.1", 80, 0, 0))]
             c = WebSocketBaseClient(url="ws://127.0.0.1/?token=value")
-            
+
             status_line = b"HTTP/1.1 101 Switching Protocols"
             headers = {
                 b"connection": b"Upgrade",
@@ -205,7 +206,7 @@
             }
 
             headers[key_header] = invalid_value
-            
+
             request = [status_line] + [k + b" : " + v for (k, v) in 
headers.items()] + [b'\r\n']
             s.recv.return_value = b"\r\n".join(request)
 
@@ -213,50 +214,70 @@
             s.shutdown.assert_called_once_with(socket.SHUT_RDWR)
             s.close.assert_called_once_with()
             sock.reset_mock()
-        
+
 class ThreadedClientTest(unittest.TestCase):
+
     @patch('ws4py.client.socket')
-    def test_thread_is_started_once_connected(self, sock):
-        s = MagicMock(spec=socket.socket)
-        sock.socket.return_value = s
+    def setUp(self, sock):
+        self.sock = MagicMock(spec=socket.socket)
+        sock.socket.return_value = self.sock
         sock.getaddrinfo.return_value = [(socket.AF_INET, socket.SOCK_STREAM, 
0, "",
                                           ("127.0.0.1", 80, 0, 0))]
- 
-        c = WebSocketClient(url="ws://127.0.0.1/")
 
-        def exchange1(*args, **kwargs):
-            yield b"\r\n".join([
-                    b"HTTP/1.1 101 Switching Protocols",
-                    b"Connection: Upgrade",
-                    b"Sec-Websocket-Version: 13",
-                    b"Content-Type: text/plain;charset=utf-8",
-                    b"Sec-Websocket-Accept: " + b64encode(sha1(c.key + 
WS_KEY).digest()),
-                    b"Upgrade: websocket",
-                    b"Date: Sun, 26 Jul 2015 12:32:55 GMT",
-                    b"Server: ws4py/test",
-                    b"\r\n"
-                ])
-
-            for i in range(100):
-                time.sleep(0.1)
-                yield Frame(opcode=OPCODE_TEXT, body=b'hello',
-                            fin=1).build()
-
-        s.recv.side_effect = exchange1()
-        self.assertFalse(c._th.is_alive())
-        
-        c.connect()
-        time.sleep(0.5)
-        self.assertTrue(c._th.is_alive())
+        self.client = WebSocketClient(url="ws://127.0.0.1/")
+
+    def _exchange1(self, *args, **kwargs):
+        yield b"\r\n".join([
+            b"HTTP/1.1 101 Switching Protocols",
+            b"Connection: Upgrade",
+            b"Sec-Websocket-Version: 13",
+            b"Content-Type: text/plain;charset=utf-8",
+            b"Sec-Websocket-Accept: " + b64encode(sha1(self.client.key + 
WS_KEY).digest()),
+            b"Upgrade: websocket",
+            b"Date: Sun, 26 Jul 2015 12:32:55 GMT",
+            b"Server: ws4py/test",
+            b"\r\n"
+        ])
 
-        def exchange2(*args, **kwargs):
-            yield Frame(opcode=OPCODE_CLOSE, body=b'',
+        for i in range(100):
+            time.sleep(0.1)
+            yield Frame(opcode=OPCODE_TEXT, body=b'hello',
                         fin=1).build()
-        s.recv.side_effect = exchange2()
+
+    def _exchange2(self, *args, **kwargs):
+        yield Frame(opcode=OPCODE_CLOSE, body=b'',
+                    fin=1).build()
+
+    def test_thread_is_started_once_connected(self):
+        self.sock.recv.side_effect = self._exchange1()
+        self.assertFalse(self.client._th.is_alive())
+
+        self.client.connect()
         time.sleep(0.5)
-        self.assertFalse(c._th.is_alive())
-        
-    
+        self.assertTrue(self.client._th.is_alive())
+
+        self.sock.recv.side_effect = self._exchange2()
+        time.sleep(0.5)
+        self.assertFalse(self.client._th.is_alive())
+
+    def test_thread_is_started_once_connected_secure(self):
+        """ Same as the above test, but with SSL socket """
+        # pretend the socket is an SSL socket
+        self.sock.pending = lambda: False
+        self.client._is_secure = True
+
+        self.sock.recv.side_effect = self._exchange1()
+        self.assertFalse(self.client._th.is_alive())
+
+        self.client.connect()
+        time.sleep(0.5)
+        self.assertTrue(self.client._th.is_alive())
+
+        self.sock.recv.side_effect = self._exchange2()
+        time.sleep(0.5)
+        self.assertFalse(self.client._th.is_alive())
+
+
 if __name__ == '__main__':
     suite = unittest.TestSuite()
     loader = unittest.TestLoader()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/__init__.py 
new/ws4py-0.5.1/ws4py/__init__.py
--- old/ws4py-0.4.2/ws4py/__init__.py   2017-03-29 19:57:05.000000000 +0200
+++ new/ws4py-0.5.1/ws4py/__init__.py   2018-02-28 17:23:16.000000000 +0100
@@ -30,7 +30,7 @@
 import logging.handlers as handlers
 
 __author__ = "Sylvain Hellegouarch"
-__version__ = "0.4.2"
+__version__ = "0.5.1"
 __all__ = ['WS_KEY', 'WS_VERSION', 'configure_logger', 'format_addresses']
 
 WS_KEY = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/async_websocket.py 
new/ws4py-0.5.1/ws4py/async_websocket.py
--- old/ws4py-0.4.2/ws4py/async_websocket.py    1970-01-01 01:00:00.000000000 
+0100
+++ new/ws4py-0.5.1/ws4py/async_websocket.py    2017-05-09 12:35:59.000000000 
+0200
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+__doc__ = """
+WebSocket implementation that relies on two new Python
+features:
+
+* asyncio to provide the high-level interface above transports
+* yield from to delegate to the reading stream whenever more
+  bytes are required
+
+You can use these implementations in that context
+and benefit from those features whilst using ws4py.
+
+Strictly speaking this module probably doesn't have to
+be called async_websocket but it feels this will be its typical
+usage and is probably more readable than
+delegated_generator_websocket_on_top_of_asyncio.py
+"""
+import asyncio
+import types
+
+from ws4py.websocket import WebSocket as _WebSocket
+from ws4py.messaging import Message
+
+__all__ = ['WebSocket', 'EchoWebSocket']
+
+class WebSocket(_WebSocket):
+    def __init__(self, proto):
+        """
+        A :pep:`3156` ready websocket handler that works
+        well in a coroutine-aware loop such as the one provided
+        by the asyncio module.
+
+        The provided `proto` instance is a
+        :class:`asyncio.Protocol` subclass instance that will
+        be used internally to read and write from the
+        underlying transport.
+
+        Because the base :class:`ws4py.websocket.WebSocket`
+        class is still coupled a bit to the socket interface,
+        we have to override a little more than necessary
+        to play nice with the :pep:`3156` interface. Hopefully,
+        some day this will be cleaned out.
+        """
+        _WebSocket.__init__(self, None)
+        self.started = False
+        self.proto = proto
+
+    @property
+    def local_address(self):
+        """
+        Local endpoint address as a tuple
+        """
+        if not self._local_address:
+            self._local_address = 
self.proto.reader.transport.get_extra_info('sockname')
+            if len(self._local_address) == 4:
+                self._local_address = self._local_address[:2]
+        return self._local_address
+
+    @property
+    def peer_address(self):
+        """
+        Peer endpoint address as a tuple
+        """
+        if not self._peer_address:
+            self._peer_address = 
self.proto.reader.transport.get_extra_info('peername')
+            if len(self._peer_address) == 4:
+                self._peer_address = self._peer_address[:2]
+        return self._peer_address
+
+    def once(self):
+        """
+        The base class directly is used in conjunction with
+        the :class:`ws4py.manager.WebSocketManager` which is
+        not actually used with the asyncio implementation
+        of ws4py. So let's make it clear it shan't be used.
+        """
+        raise NotImplemented()
+
+    def close_connection(self):
+        """
+        Close the underlying transport
+        """
+        @asyncio.coroutine
+        def closeit():
+            yield from self.proto.writer.drain()
+            self.proto.writer.close()
+        asyncio.async(closeit())
+
+    def _write(self, data):
+        """
+        Write to the underlying transport
+        """
+        @asyncio.coroutine
+        def sendit(data):
+            self.proto.writer.write(data)
+            yield from self.proto.writer.drain()
+        asyncio.async(sendit(data))
+
+    @asyncio.coroutine
+    def run(self):
+        """
+        Coroutine that runs until the websocket
+        exchange is terminated. It also calls the
+        `opened()` method to indicate the exchange
+        has started.
+        """
+        self.started = True
+        try:
+            self.opened()
+            reader = self.proto.reader
+            while True:
+                data = yield from reader.read(self.reading_buffer_size)
+                if not self.process(data):
+                    return False
+        finally:
+            self.terminate()
+
+        return True
+
+class EchoWebSocket(WebSocket):
+    def received_message(self, message):
+        """
+        Automatically sends back the provided ``message`` to
+        its originating endpoint.
+        """
+        self.send(message.data, message.is_binary)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/client/__init__.py 
new/ws4py-0.5.1/ws4py/client/__init__.py
--- old/ws4py-0.4.2/ws4py/client/__init__.py    2017-03-26 20:42:52.000000000 
+0200
+++ new/ws4py-0.5.1/ws4py/client/__init__.py    2018-02-28 17:21:12.000000000 
+0100
@@ -14,7 +14,7 @@
 
 class WebSocketBaseClient(WebSocket):
     def __init__(self, url, protocols=None, extensions=None,
-                 heartbeat_freq=None, ssl_options=None, headers=None):
+                 heartbeat_freq=None, ssl_options=None, headers=None, 
exclude_headers=None):
         """
         A websocket client that implements :rfc:`6455` and provides a simple
         interface to communicate with a websocket server.
@@ -36,7 +36,7 @@
 
         .. code-block:: python
 
-           >>> from websocket.client import WebSocketBaseClient
+           >>> from ws4py.client import WebSocketBaseClient
            >>> ws = WebSocketBaseClient('ws://localhost/ws')
 
 
@@ -44,7 +44,7 @@
 
         .. code-block:: python
 
-           >>> from websocket.client import WebSocketBaseClient
+           >>> from ws4py.client import WebSocketBaseClient
            >>> ws = WebSocketBaseClient('wss://localhost/ws')
 
 
@@ -52,7 +52,7 @@
 
         .. code-block:: python
 
-           >>> from websocket.client import WebSocketBaseClient
+           >>> from ws4py.client import WebSocketBaseClient
            >>> ws = WebSocketBaseClient('ws+unix:///tmp/my.sock')
 
         Note that in this case, the initial Upgrade request
@@ -61,7 +61,7 @@
 
         .. code-block:: python
 
-           >>> from websocket.client import WebSocketBaseClient
+           >>> from ws4py.client import WebSocketBaseClient
            >>> ws = WebSocketBaseClient('ws+unix:///tmp/my.sock')
            >>> ws.resource = '/ws'
            >>> ws.connect()
@@ -78,6 +78,8 @@
         self.resource = None
         self.ssl_options = ssl_options or {}
         self.extra_headers = headers or []
+        self.exclude_headers = exclude_headers or []
+        self.exclude_headers = [x.lower() for x in self.exclude_headers]
 
         if self.scheme == "wss":
             # Prevent check_hostname requires server_hostname (ref #187)
@@ -211,7 +213,7 @@
             # default port is now 443; upgrade self.sender to send ssl
             self.sock = ssl.wrap_socket(self.sock, **self.ssl_options)
             self._is_secure = True
-            
+
         self.sock.connect(self.bind_addr)
 
         self._write(self.handshake_request)
@@ -257,14 +259,15 @@
             ('Sec-WebSocket-Key', self.key.decode('utf-8')),
             ('Sec-WebSocket-Version', str(max(WS_VERSION)))
             ]
-        
+
         if self.protocols:
             headers.append(('Sec-WebSocket-Protocol', 
','.join(self.protocols)))
 
         if self.extra_headers:
             headers.extend(self.extra_headers)
 
-        if not any(x for x in headers if x[0].lower() == 'origin'):
+        if not any(x for x in headers if x[0].lower() == 'origin') and \
+           'origin' not in self.exclude_headers:
 
             scheme, url = self.url.split(":", 1)
             parsed = urlsplit(url, scheme="http")
@@ -277,6 +280,8 @@
                 origin = origin + ':' + str(parsed.port)
             headers.append(('Origin', origin))
 
+        headers = [x for x in headers if x[0].lower() not in 
self.exclude_headers]
+
         return headers
 
     @property
@@ -328,10 +333,10 @@
                     raise HandshakeError("Invalid challenge response: %s" % 
value)
 
             elif header == b'sec-websocket-protocol':
-                protocols = ','.join(value)
+                protocols.extend([x.strip() for x in value.split(b',')])
 
             elif header == b'sec-websocket-extensions':
-                extensions = ','.join(value)
+                extensions.extend([x.strip() for x in value.split(b',')])
 
         return protocols, extensions
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/client/geventclient.py 
new/ws4py-0.5.1/ws4py/client/geventclient.py
--- old/ws4py-0.4.2/ws4py/client/geventclient.py        2017-03-23 
00:21:52.000000000 +0100
+++ new/ws4py-0.5.1/ws4py/client/geventclient.py        2018-02-27 
08:55:33.000000000 +0100
@@ -10,7 +10,7 @@
 __all__ = ['WebSocketClient']
 
 class WebSocketClient(WebSocketBaseClient):
-    def __init__(self, url, protocols=None, extensions=None, 
heartbeat_freq=None, ssl_options=None, headers=None):
+    def __init__(self, url, protocols=None, extensions=None, 
heartbeat_freq=None, ssl_options=None, headers=None, exclude_headers=None):
         """
         WebSocket client that executes the
         :meth:`run() <ws4py.websocket.WebSocket.run>` into a gevent greenlet.
@@ -41,7 +41,7 @@
           gevent.joinall(greenlets)
         """
         WebSocketBaseClient.__init__(self, url, protocols, extensions, 
heartbeat_freq,
-                                     ssl_options=ssl_options, headers=headers)
+                                     ssl_options=ssl_options, headers=headers, 
exclude_headers=exclude_headers)
         self._th = Greenlet(self.run)
 
         self.messages = Queue()
@@ -75,18 +75,22 @@
         # to wait for
         self.messages.put(StopIteration)
 
-    def receive(self):
+    def receive(self, block=True):
         """
         Returns messages that were stored into the
         `messages` queue and returns `None` when the
         websocket is terminated or closed.
+        `block` is passed though the gevent queue `.get()` method, which if 
+        True will block until an item in the queue is available. Set this to 
+        False if you just want to check the queue, which will raise an 
+        Empty exception you need to handle if there is no message to return.
         """
         # If the websocket was terminated and there are no messages
         # left in the queue, return None immediately otherwise the client
         # will block forever
         if self.terminated and self.messages.empty():
             return None
-        message = self.messages.get()
+        message = self.messages.get(block=block)
         if message is StopIteration:
             return None
         return message
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/client/threadedclient.py 
new/ws4py-0.5.1/ws4py/client/threadedclient.py
--- old/ws4py-0.4.2/ws4py/client/threadedclient.py      2017-03-23 
00:21:52.000000000 +0100
+++ new/ws4py-0.5.1/ws4py/client/threadedclient.py      2017-10-17 
12:52:58.000000000 +0200
@@ -7,7 +7,7 @@
 
 class WebSocketClient(WebSocketBaseClient):
     def __init__(self, url, protocols=None, extensions=None, 
heartbeat_freq=None,
-                 ssl_options=None, headers=None):
+                 ssl_options=None, headers=None, exclude_headers=None):
         """
         .. code-block:: python
 
@@ -32,7 +32,7 @@
 
         """
         WebSocketBaseClient.__init__(self, url, protocols, extensions, 
heartbeat_freq,
-                                     ssl_options, headers=headers)
+                                     ssl_options, headers=headers, 
exclude_headers=exclude_headers)
         self._th = threading.Thread(target=self.run, name='WebSocketClient')
         self._th.daemon = True
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/client/tornadoclient.py 
new/ws4py-0.5.1/ws4py/client/tornadoclient.py
--- old/ws4py-0.4.2/ws4py/client/tornadoclient.py       2017-03-23 
00:21:52.000000000 +0100
+++ new/ws4py-0.5.1/ws4py/client/tornadoclient.py       2017-10-17 
12:52:58.000000000 +0200
@@ -9,7 +9,7 @@
 
 class TornadoWebSocketClient(WebSocketBaseClient):
     def __init__(self, url, protocols=None, extensions=None,
-                 io_loop=None, ssl_options=None, headers=None):
+                 io_loop=None, ssl_options=None, headers=None, 
exclude_headers=None):
         """
         .. code-block:: python
 
@@ -32,7 +32,7 @@
             ioloop.IOLoop.instance().start()
         """
         WebSocketBaseClient.__init__(self, url, protocols, extensions,
-                                     ssl_options=ssl_options, headers=headers)
+                                     ssl_options=ssl_options, headers=headers, 
exclude_headers=exclude_headers)
         if self.scheme == "wss":
             self.sock = ssl.wrap_socket(self.sock, 
do_handshake_on_connect=False, **self.ssl_options)
             self._is_secure = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/server/cherrypyserver.py 
new/ws4py-0.5.1/ws4py/server/cherrypyserver.py
--- old/ws4py-0.4.2/ws4py/server/cherrypyserver.py      2017-03-23 
00:21:52.000000000 +0100
+++ new/ws4py-0.5.1/ws4py/server/cherrypyserver.py      2017-12-19 
10:15:47.000000000 +0100
@@ -272,14 +272,14 @@
                 break
             _locals = current.f_locals
             if 'self' in _locals:
-               if type(_locals['self']) == HTTPRequest:
-                   _locals['self'].close_connection = True
-               if type(_locals['self']) == HTTPConnection:
-                   _locals['self'].linger = True
-                   # HTTPConnection is more inner than
-                   # HTTPRequest so we can leave once
-                   # we're done here
-                   return
+                if isinstance(_locals['self'], HTTPRequest):
+                    _locals['self'].close_connection = True
+                if isinstance(_locals['self'], HTTPConnection):
+                    _locals['self'].linger = True
+                    # HTTPConnection is more inner than
+                    # HTTPRequest so we can leave once
+                    # we're done here
+                    return
             _locals = None
             current = current.f_back
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/server/geventserver.py 
new/ws4py-0.5.1/ws4py/server/geventserver.py
--- old/ws4py-0.4.2/ws4py/server/geventserver.py        2017-03-23 
00:21:52.000000000 +0100
+++ new/ws4py-0.5.1/ws4py/server/geventserver.py        2018-02-27 
08:55:33.000000000 +0100
@@ -78,7 +78,7 @@
 
     def clear(self):
         logger.info("Terminating server and all connected websockets")
-        for greenlet in self:
+        for greenlet in list(self):
             try:
                 websocket = greenlet._run.im_self
                 if websocket:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/server/tulipserver.py 
new/ws4py-0.5.1/ws4py/server/tulipserver.py
--- old/ws4py-0.4.2/ws4py/server/tulipserver.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/ws4py-0.5.1/ws4py/server/tulipserver.py 2017-05-09 12:35:59.000000000 
+0200
@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+import base64
+from hashlib import sha1
+from email.parser import BytesHeaderParser
+import io
+
+import asyncio
+
+from ws4py import WS_KEY, WS_VERSION
+from ws4py.exc import HandshakeError
+from ws4py.websocket import WebSocket
+
+LF = b'\n'
+CRLF = b'\r\n'
+SPACE = b' '
+EMPTY = b''
+
+__all__ = ['WebSocketProtocol']
+
+class WebSocketProtocol(asyncio.StreamReaderProtocol):
+    def __init__(self, handler_cls):
+        asyncio.StreamReaderProtocol.__init__(self, asyncio.StreamReader(),
+                                              self._pseudo_connected)
+        self.ws = handler_cls(self)
+
+    def _pseudo_connected(self, reader, writer):
+        pass
+        
+    def connection_made(self, transport):
+        """
+        A peer is now connected and we receive an instance
+        of the underlying :class:`asyncio.Transport`.
+
+        We :class:`asyncio.StreamReader` is created
+        and the transport is associated before the
+        initial HTTP handshake is undertaken.
+        """
+        #self.transport = transport
+        #self.stream = asyncio.StreamReader()
+        #self.stream.set_transport(transport)
+        asyncio.StreamReaderProtocol.connection_made(self, transport)
+        # Let make it concurrent for others to tag along
+        f = asyncio.async(self.handle_initial_handshake())
+        f.add_done_callback(self.terminated)
+
+    @property
+    def writer(self):
+        return self._stream_writer
+        
+    @property
+    def reader(self):
+        return self._stream_reader
+        
+    def terminated(self, f):
+        if f.done() and not f.cancelled():
+            ex = f.exception()
+            if ex:
+                response = [b'HTTP/1.0 400 Bad Request']
+                response.append(b'Content-Length: 0')
+                response.append(b'Connection: close')
+                response.append(b'')
+                response.append(b'')
+                self.writer.write(CRLF.join(response))
+                self.ws.close_connection()
+
+    def close(self):
+        """
+        Initiate the websocket closing handshake
+        which will eventuall lead to the underlying
+        transport.
+        """
+        self.ws.close()
+        
+    def timeout(self):
+        self.ws.close_connection()
+        if self.ws.started:
+            self.ws.closed(1002, "Peer connection timed-out")
+        
+    def connection_lost(self, exc):
+        """
+        The peer connection is now, the closing
+        handshake won't work so let's not even try.
+        However let's make the websocket handler
+        be aware of it by calling its `closed`
+        method.
+        """
+        if exc is not None:
+            self.ws.close_connection()
+            if self.ws.started:
+                self.ws.closed(1002, "Peer connection was lost")
+            
+    @asyncio.coroutine
+    def handle_initial_handshake(self):
+        """
+        Performs the HTTP handshake described in :rfc:`6455`. Note that
+        this implementation is really basic and it is strongly advised
+        against using it in production. It would probably break for
+        most clients. If you want a better support for HTTP, please
+        use a more reliable HTTP server implemented using asyncio.
+        """
+        request_line = yield from self.next_line()
+        method, uri, req_protocol = request_line.strip().split(SPACE, 2)
+        
+        # GET required
+        if method.upper() != b'GET':
+            raise HandshakeError('HTTP method must be a GET')
+        
+        headers = yield from self.read_headers()
+        if req_protocol == b'HTTP/1.1' and 'Host' not in headers:
+            raise ValueError("Missing host header")
+        
+        for key, expected_value in [('Upgrade', 'websocket'),
+                                     ('Connection', 'upgrade')]:
+            actual_value = headers.get(key, '').lower()
+            if not actual_value:
+                raise HandshakeError('Header %s is not defined' % str(key))
+            if expected_value not in actual_value:
+                raise HandshakeError('Illegal value for header %s: %s' %
+                                     (key, actual_value))
+
+        response_headers = {}
+
+        ws_version = WS_VERSION
+        version = headers.get('Sec-WebSocket-Version')
+        supported_versions = ', '.join([str(v) for v in ws_version])
+        version_is_valid = False
+        if version:
+            try: version = int(version)
+            except: pass
+            else: version_is_valid = version in ws_version
+
+        if not version_is_valid:
+            response_headers['Sec-WebSocket-Version'] = supported_versions
+            raise HandshakeError('Unhandled or missing WebSocket version')
+
+        key = headers.get('Sec-WebSocket-Key')
+        if key:
+            ws_key = base64.b64decode(key.encode('utf-8'))
+            if len(ws_key) != 16:
+                raise HandshakeError("WebSocket key's length is invalid")
+
+        protocols = []
+        ws_protocols = []
+        subprotocols = headers.get('Sec-WebSocket-Protocol')
+        if subprotocols:
+            for s in subprotocols.split(','):
+                s = s.strip()
+                if s in protocols:
+                    ws_protocols.append(s)
+
+        exts = []
+        ws_extensions = []
+        extensions = headers.get('Sec-WebSocket-Extensions')
+        if extensions:
+            for ext in extensions.split(','):
+                ext = ext.strip()
+                if ext in exts:
+                    ws_extensions.append(ext)
+
+        self.ws.protocols = ws_protocols
+        self.ws.extensions = ws_extensions
+        self.ws.headers = headers
+                    
+        response = [req_protocol + b' 101 Switching Protocols']
+        response.append(b'Upgrade: websocket')
+        response.append(b'Content-Type: text/plain')
+        response.append(b'Content-Length: 0')
+        response.append(b'Connection: Upgrade')
+        response.append(b'Sec-WebSocket-Version:' + bytes(str(version), 
'utf-8'))
+        response.append(b'Sec-WebSocket-Accept:' + 
base64.b64encode(sha1(key.encode('utf-8') + WS_KEY).digest()))
+        if ws_protocols:
+            response.append(b'Sec-WebSocket-Protocol:' + b', 
'.join(ws_protocols))
+        if ws_extensions:
+            response.append(b'Sec-WebSocket-Extensions:' + 
b','.join(ws_extensions))
+        response.append(b'')
+        response.append(b'')
+        self.writer.write(CRLF.join(response))
+        yield from self.handle_websocket()
+
+    @asyncio.coroutine
+    def handle_websocket(self):
+        """
+        Starts the websocket process until the
+        exchange is completed and terminated.
+        """
+        yield from self.ws.run()
+        
+    @asyncio.coroutine
+    def read_headers(self):
+        """
+        Read all HTTP headers from the HTTP request
+        and returns a dictionary of them.
+        """
+        headers = b''
+        while True:
+            line = yield from self.next_line()
+            headers += line
+            if line == CRLF:
+                break
+        return BytesHeaderParser().parsebytes(headers)
+        
+    @asyncio.coroutine
+    def next_line(self):
+        """
+        Reads data until \r\n is met and then return all read
+        bytes. 
+        """
+        line = yield from self.reader.readline()
+        if not line.endswith(CRLF):
+            raise ValueError("Missing mandatory trailing CRLF")
+        return line
+        
+if __name__ == '__main__':
+    from ws4py.async_websocket import EchoWebSocket
+    
+    loop = asyncio.get_event_loop()
+
+    def start_server():
+        proto_factory = lambda: WebSocketProtocol(EchoWebSocket)
+        return loop.create_server(proto_factory, '', 9007)
+
+    s = loop.run_until_complete(start_server())
+    print('serving on', s.sockets[0].getsockname())
+    loop.run_forever()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py/websocket.py 
new/ws4py-0.5.1/ws4py/websocket.py
--- old/ws4py-0.4.2/ws4py/websocket.py  2017-03-29 19:56:41.000000000 +0200
+++ new/ws4py-0.5.1/ws4py/websocket.py  2018-02-27 08:55:33.000000000 +0100
@@ -387,10 +387,12 @@
             logger.debug("WebSocket is already terminated")
             return False
         try:
-            b = self.sock.recv(self.reading_buffer_size)
+            b = b''
             if self._is_secure:
-                b += self._get_from_pending()
-            if not b:
+                b = self._get_from_pending()
+            if not b and not self.buf:
+                b = self.sock.recv(self.reading_buffer_size)
+            if not b and not self.buf:
                 return False
             self.buf += b
         except (socket.error, OSError, pyOpenSSLError) as e:
@@ -403,9 +405,11 @@
             # process as much as we can
             # the process will stop either if there is no buffer left
             # or if the stream is closed
-            if not self.process(self.buf):
+            # only pass the requested number of bytes, leave the rest in the 
buffer
+            requested = self.reading_buffer_size
+            if not self.process(self.buf[:requested]):
                 return False
-            self.buf = b""
+            self.buf = self.buf[requested:]
 
         return True
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py.egg-info/PKG-INFO 
new/ws4py-0.5.1/ws4py.egg-info/PKG-INFO
--- old/ws4py-0.4.2/ws4py.egg-info/PKG-INFO     2017-03-29 19:57:06.000000000 
+0200
+++ new/ws4py-0.5.1/ws4py.egg-info/PKG-INFO     2018-02-28 18:40:21.000000000 
+0100
@@ -1,12 +1,13 @@
 Metadata-Version: 1.1
 Name: ws4py
-Version: 0.4.2
+Version: 0.5.1
 Summary: WebSocket client and server library for Python 2 and 3 as well as PyPy
 Home-page: https://github.com/Lawouach/WebSocket-for-Python
 Author: Sylvain Hellegouarch
 Author-email: [email protected]
 License: BSD
 Download-URL: https://pypi.python.org/pypi/ws4py
+Description-Content-Type: UNKNOWN
 Description: WebSocket client and server library for Python 2 and 3 as well as 
PyPy
 Platform: any
 Classifier: Development Status :: 5 - Production/Stable
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ws4py-0.4.2/ws4py.egg-info/SOURCES.txt 
new/ws4py-0.5.1/ws4py.egg-info/SOURCES.txt
--- old/ws4py-0.4.2/ws4py.egg-info/SOURCES.txt  2017-03-29 19:57:06.000000000 
+0200
+++ new/ws4py-0.5.1/ws4py.egg-info/SOURCES.txt  2018-02-28 18:40:21.000000000 
+0100
@@ -1,3 +1,7 @@
+CHANGELOG.md
+LICENSE
+MANIFEST.in
+README.md
 setup.py
 test/test_cherrypy.py
 test/test_client.py
@@ -9,6 +13,7 @@
 test/test_utils.py
 test/test_websocket.py
 ws4py/__init__.py
+ws4py/async_websocket.py
 ws4py/compat.py
 ws4py/exc.py
 ws4py/framing.py
@@ -28,5 +33,6 @@
 ws4py/server/__init__.py
 ws4py/server/cherrypyserver.py
 ws4py/server/geventserver.py
+ws4py/server/tulipserver.py
 ws4py/server/wsgirefserver.py
 ws4py/server/wsgiutils.py
\ No newline at end of file


Reply via email to