Skip to content

Commit 62b7797

Browse files
committed
various docstrings updates
1 parent 3a28e6e commit 62b7797

8 files changed

Lines changed: 184 additions & 155 deletions

File tree

ws4py/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929

3030
__author__ = "Sylvain Hellegouarch"
3131
__version__ = "0.2.0"
32-
__all__ = ['WS_KEY']
32+
__all__ = ['WS_KEY', 'WS_VERSION']
3333

3434
WS_KEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
35+
WS_VERSION = 13
3536

ws4py/framing.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,19 @@
1818
class Frame(object):
1919
def __init__(self, opcode=None, body='', masking_key=None, fin=0, rsv1=0, rsv2=0, rsv3=0):
2020
"""
21-
Implements the framing protocol as defined by draft-10 of the specification
22-
supporting protocol version 8.
23-
24-
>>> f = Frame(OPCODE_TEXT, 'hello world', os.urandom(4), fin=1)
25-
>>> bytes = f.build()
26-
>>> f = Frame()
27-
>>> f.parser.send(bytes[1])
28-
>>> f.parser.send(bytes[2])
29-
>>> f.parser.send(bytes[2:])
21+
Implements the framing protocol as defined by RFC 6455.
22+
23+
.. code-block:: python
24+
:linenos:
25+
26+
>>> f = Frame(OPCODE_TEXT, 'hello world', os.urandom(4), fin=1)
27+
>>> bytes = f.build()
28+
>>> f = Frame()
29+
>>> f.parser.send(bytes[1])
30+
>>> f.parser.send(bytes[2])
31+
>>> f.parser.send(bytes[2:])
32+
33+
.. seealso:: Data Framing http://tools.ietf.org/html/rfc6455#section-5.2
3034
"""
3135
self.opcode = opcode
3236
self.body = body
@@ -42,9 +46,8 @@ def __init__(self, opcode=None, body='', masking_key=None, fin=0, rsv1=0, rsv2=0
4246

4347
def build(self):
4448
"""
45-
Builds a frame from the instance's attributes.
46-
47-
@return: The frame header and payload as bytes.
49+
Builds a frame from the instance's attributes and returns
50+
its bytes representation.
4851
"""
4952
header = ''
5053

@@ -232,15 +235,15 @@ def _parser(self):
232235

233236
self.body = bytes
234237

235-
#yield
236-
237238
def mask(self, data):
238239
"""
239240
Performs the masking or unmasking operation on data
240-
using the simple masking algorithme:
241+
using the simple masking algorithm:
241242
242-
j = i MOD 4
243-
transformed-octet-i = original-octet-i XOR masking-key-octet-j
243+
..
244+
j = i MOD 4
245+
transformed-octet-i = original-octet-i XOR masking-key-octet-j
246+
244247
"""
245248
masked = bytearray(data)
246249
key = map(ord, self.masking_key)

ws4py/messaging.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,27 @@
55
from ws4py.framing import Frame, OPCODE_CONTINUATION, OPCODE_TEXT, \
66
OPCODE_BINARY, OPCODE_CLOSE, OPCODE_PING, OPCODE_PONG
77

8-
__all__ = ['TextMessage', 'BinaryMessage', 'CloseControlMessage',
8+
__all__ = ['Message', 'TextMessage', 'BinaryMessage', 'CloseControlMessage',
99
'PingControlMessage', 'PongControlMessage']
1010

1111
class Message(object):
12-
def __init__(self, opcode, data='', encoding='utf-8', size=None):
12+
def __init__(self, opcode, data='', encoding='utf-8'):
1313
"""
14-
A WebSocket message is made of an opcode defining its type
15-
and some bytes.
14+
A message is a application level entity. It's usually built
15+
from one or many frames. The protocol defines several kind
16+
of messages which are grouped into two sets:
1617
17-
@param opcode: message type
18-
@param data: message bytes
19-
@param encoding: how to encode the message bytes
18+
* data messages which can be text or binary typed
19+
* control messages which provide a mechanism to perform
20+
in-band control communication between peers
21+
22+
The ``opcode`` indicates the message type and ``data`` is
23+
the possible message payload.
24+
25+
The payload is held internally as a a :func:`bytearray` as they are
26+
faster than pure strings for append operations.
27+
28+
Unicode data will be encoded using the provided ``encoding``.
2029
"""
2130
self.opcode = opcode
2231
self._completed = False
@@ -36,18 +45,23 @@ def __init__(self, opcode, data='', encoding='utf-8', size=None):
3645
def single(self, mask=False):
3746
"""
3847
Returns a frame bytes with the fin bit set and a random mask.
48+
49+
If ``mask`` is set, automatically mask the frame
50+
using a generated 4-byte token.
3951
"""
4052
mask = os.urandom(4) if mask else None
4153
return Frame(body=self.data or '', opcode=self.opcode,
4254
masking_key=mask, fin=1).build()
4355

4456
def fragment(self, first=False, last=False, mask=False):
4557
"""
46-
Returns a frame bytes as part of a fragmented message.
58+
Returns a :class:`ws4py.framing.Frame` bytes.
59+
60+
The behavior depends on the given flags:
4761
48-
@param first: indicates this is the first frame of the message
49-
@param last: indicates this is the last frame of the message,
50-
setting the fin bit
62+
* ``first``: the frame uses ``self.opcode`` else a continuation opcode
63+
* ``last``: the frame has its ``fin`` bit set
64+
* ``mask``: the frame is masked using a automatically generated 4-byte token
5165
"""
5266
fin = 1 if last is True else 0
5367
opcode = self.opcode if first is True else OPCODE_CONTINUATION
@@ -60,7 +74,7 @@ def fragment(self, first=False, last=False, mask=False):
6074
def completed(self):
6175
"""
6276
Indicates the the message is complete, meaning
63-
the frame fin bit was set for its last frame.
77+
the frame's ``fin`` bit was set.
6478
"""
6579
return self._completed
6680

@@ -74,9 +88,7 @@ def completed(self, state):
7488

7589
def extend(self, data):
7690
"""
77-
Add more bytes to the message.
78-
79-
@param: bytes to add to this message.
91+
Add more ``data`` to the message.
8092
"""
8193
if isinstance(data, unicode):
8294
data = data.encode(self.encoding)

ws4py/server/cherrypyserver.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
of some internals of CherryPy as well as the introspection
1010
Python provides.
1111
12-
Basically, whene the WebSocket upgrade is performed, we take over
12+
Basically, when the WebSocket handshake is complete, we take over
1313
the socket and let CherryPy take back the thread that was
1414
associated with the upgrade request.
1515
@@ -26,15 +26,19 @@
2626
any path you wish to handle as a WebSocket
2727
handler.
2828
29-
* WebSocketPlugin: The plugin tracks the web socket handler
30-
instanciated. It also cleans out websocket handler
31-
which connection have been closed down.
29+
* WebSocketPlugin: The plugin tracks the instanciated web socket handlers.
30+
It also cleans out websocket handler which connection
31+
have been closed down. The websocket connection then
32+
runs in its own thread that this plugin manages.
3233
3334
Simple usage example:
3435
36+
.. code-block:: python
37+
:linenos:
38+
3539
import cherrypy
36-
from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool, WebSocketHandler
37-
from ws4py.server.handler.threadedhandler import EchoWebSocketHandler
40+
from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool
41+
from ws4py.websocket import EchoWebSocket
3842
3943
cherrypy.config.update({'server.socket_port': 9000})
4044
WebSocketPlugin(cherrypy.engine).subscribe()
@@ -50,18 +54,12 @@ def ws(self):
5054
pass
5155
5256
cherrypy.quickstart(Root(), '/', config={'/ws': {'tools.websocket.on': True,
53-
'tools.websocket.handler_cls': EchoWebSocketHandler}})
57+
'tools.websocket.handler_cls': EchoWebSocket}})
5458
5559
5660
Note that you can set the handler class on per-path basis,
5761
meaning you could also dynamically change the class based
5862
on other envrionmental settings (is the user authenticated for ex).
59-
60-
The current implementation of the handler is based on a thread that will
61-
constantly read bytes from the socket and feed the stream instance with them
62-
until an error or a close condition arise. This might be a bit
63-
suboptimal and one could implement the handler in a different fashion
64-
using a poll based socket handling (select, poll, tornado, gevent, etc.)
6563
"""
6664
import base64
6765
from hashlib import sha1
@@ -132,19 +130,21 @@ def upgrade(self, protocols=None, extensions=None, version=13, handler_cls=WebSo
132130
raise HandshakeError('Illegal value for header %s: %s' %
133131
(key, actual_value))
134132

135-
key = request.headers.get('Sec-WebSocket-Key')
136-
if key:
137-
ws_key = base64.b64decode(key)
138-
if len(ws_key) != 16:
139-
raise HandshakeError("WebSocket key's length is invalid")
140-
141133
version = request.headers.get('Sec-WebSocket-Version')
142134
if version:
143135
if version != str(ws_version):
136+
cherrypy.response.headers['Sec-WebSocket-Version'] = str(ws_version)
144137
raise HandshakeError('Unsupported WebSocket version: %s' % version)
145138
else:
139+
cherrypy.response.headers['Sec-WebSocket-Version'] = str(ws_version)
146140
raise HandshakeError('WebSocket version required')
147141

142+
key = request.headers.get('Sec-WebSocket-Key')
143+
if key:
144+
ws_key = base64.b64decode(key)
145+
if len(ws_key) != 16:
146+
raise HandshakeError("WebSocket key's length is invalid")
147+
148148
protocols = protocols or []
149149
subprotocols = request.headers.get('Sec-WebSocket-Protocol')
150150
if subprotocols:

ws4py/server/geventserver.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
from gevent import monkey; monkey.patch_all()
3-
2+
import gevent
43
import gevent.pywsgi
54
from gevent import version_info
65
IS_GEVENT_V10 = version_info[0] == 1
@@ -95,10 +94,17 @@ def __init__(self, address, *args, **kwargs):
9594
websocket = kwargs.pop('websocket_class', WebSocket)
9695

9796
gevent.pywsgi.WSGIServer.__init__(self, address, *args, **kwargs)
98-
self.application = WebSocketUpgradeMiddleware(protocols=protocols,
97+
self.application = WebSocketUpgradeMiddleware(app=self.handler,
98+
protocols=protocols,
9999
extensions=extensions,
100100
websocket_class=websocket)
101101

102+
def handler(self, websocket):
103+
g = gevent.spawn(websocket.run)
104+
g.start()
105+
g.join()
106+
return ['']
107+
102108
if __name__ == '__main__':
103109
import logging
104110
import sys

ws4py/server/wsgi/middleware.py

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
1+
# -*- coding: utf-8 -*-
12
import copy
23
import base64
34
from hashlib import sha1
45
import types
56
import socket
67

7-
import gevent
8-
from gevent.coros import Semaphore as Lock
9-
from gevent.queue import Queue
10-
11-
from ws4py import WS_KEY
8+
from ws4py import WS_KEY, WS_VERSION
129
from ws4py.exc import HandshakeError, StreamClosed
1310
from ws4py.streaming import Stream
1411
from ws4py.websocket import WebSocket
1512

16-
WS_VERSION = 13
17-
1813
class WebSocketUpgradeMiddleware(object):
1914
"""WSGI middleware for handling WebSocket upgrades"""
2015

21-
def __init__(self, fallback_app=None, protocols=None, extensions=None,
16+
def __init__(self, app, fallback_app=None, protocols=None, extensions=None,
2217
websocket_class=WebSocket):
18+
self.app = app
2319
self.fallback_app = fallback_app
2420
self.protocols = protocols
2521
self.extensions = extensions
@@ -53,6 +49,7 @@ def __call__(self, environ, start_response):
5349
if self.fallback_app:
5450
return self.fallback_app(environ, start_response)
5551
else:
52+
print e
5653
start_response("400 Bad Handshake", [])
5754
return [str(e)]
5855

@@ -89,12 +86,8 @@ def __call__(self, environ, start_response):
8986
headers.append(('Sec-WebSocket-Extensions', ','.join(ws_extensions)))
9087

9188
start_response("101 Web Socket Hybi Handshake", headers)
92-
93-
ws = self.websocket_class(environ.get('upgrade.socket'),
94-
ws_protocols,
95-
ws_extensions,
96-
environ.copy())
97-
98-
g = gevent.spawn(ws.run)
99-
g.start()
100-
g.join()
89+
90+
return self.app(self.websocket_class(environ.get('upgrade.socket'),
91+
ws_protocols,
92+
ws_extensions,
93+
environ.copy()))

0 commit comments

Comments
 (0)