Hello community,
here is the log from the commit of package python-ws4py for openSUSE:Factory checked in at 2019-04-12 09:15:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ws4py (Old)
and /work/SRC/openSUSE:Factory/.python-ws4py.new.27019 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ws4py"
Fri Apr 12 09:15:47 2019 rev:3 rq:693259 version:0.5.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-ws4py/python-ws4py.changes 2018-12-13 19:45:20.904941974 +0100
+++ /work/SRC/openSUSE:Factory/.python-ws4py.new.27019/python-ws4py.changes 2019-04-12 09:15:49.241729208 +0200
@@ -1,0 +2,11 @@
+Thu Apr 11 12:09:24 UTC 2019 - Marketa Calabkova
+
+- 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
+
+-------------------------------------------------------------------
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.kpMuDX/_old 2019-04-12 09:15:50.249729819 +0200
+++ /var/tmp/diff_new_pack.kpMuDX/_new 2019-04-12 09:15:50.249729819 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-ws4py
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,26 +17,34 @@
%{?!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
Summary: WebSocket client and server library for Python
License: BSD-2-Clause
Group: Development/Languages/Python
-Url: https://github.com/Lawouach/WebSocket-for-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
+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....)
+
+**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-bet...)
+
+**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: sh@defuze.org
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() ` 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: sh@defuze.org
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