Added documentation for websocket module and switched to using decorators in the examples.

This commit is contained in:
Ryan Williams
2010-05-22 14:19:30 -07:00
parent 0363e0b40a
commit 800883ee1b
4 changed files with 47 additions and 19 deletions

View File

@@ -73,3 +73,14 @@ Producer Consumer/Recursive Web Crawler
This is an example implementation of the producer/consumer pattern as well as a functional recursive web crawler.
.. literalinclude:: ../examples/producer_consumer.py
.. _websocket_example:
Websocket Server Example
--------------------------
``examples/websocket.py``
This exercises some of the features of the websocket server
implementation.
.. literalinclude:: ../examples/websocket.py

View File

@@ -15,4 +15,5 @@ Module Reference
modules/queue
modules/semaphore
modules/timeout
modules/websocket
modules/wsgi

View File

@@ -9,8 +9,22 @@ from eventlet.support import get_errno
ACCEPTABLE_CLIENT_ERRORS = set((errno.ECONNRESET, errno.EPIPE))
__all__ = ["WebSocketWSGI", "WebSocket"]
class WebSocketWSGI(object):
"""This is a WSGI application that serves up websocket connections.
"""Wraps a websocket handler function in a WSGI application.
Use it like this::
@websocket.WebSocketWSGI
def my_handler(ws):
from_browser = ws.wait()
ws.send("from server")
The single argument to the function will be an instance of
:class:`WebSocket`. To close the socket, simply return from the
function. Note that the server will log the websocket request at
the time of closure.
"""
def __init__(self, handler):
self.handler = handler
@@ -29,9 +43,9 @@ class WebSocketWSGI(object):
"Connection: Upgrade\r\n"
"WebSocket-Origin: %s\r\n"
"WebSocket-Location: ws://%s%s\r\n\r\n" % (
environ.get('HTTP_ORIGIN'),
environ.get('HTTP_HOST'),
environ.get('PATH_INFO')))
environ.get('HTTP_ORIGIN'),
environ.get('HTTP_HOST'),
environ.get('PATH_INFO')))
sock.sendall(handshake_reply)
try:
self.handler(ws)
@@ -42,22 +56,24 @@ class WebSocketWSGI(object):
# doesn't barf on the fact that we didn't call start_response
return wsgi.ALREADY_HANDLED
class WebSocket(object):
"""The object representing the server side of a websocket.
"""A websocket object that handles the details of
serialization/deserialization to the socket.
The primary way to interact with a WebSocket object is to call
:meth:`send` and :meth:`wait` in order to pass messages back and
forth with the client. Also available are the following properties:
The primary way to interact with a :class:`WebSocket` object is to
call :meth:`send` and :meth:`wait` in order to pass messages back
and forth with the browser. Also available are the following
properties:
path
The path value of the request. This is the same as the WSGI PATH_INFO variable.
The path value of the request. This is the same as the WSGI PATH_INFO variable, but more convenient.
protocol
The value of the Websocket-Protocol header.
origin
The value of the 'Origin' header.
environ
The full WSGI environment for this request.
"""
def __init__(self, sock, environ):
"""
@@ -75,7 +91,7 @@ class WebSocket(object):
self._sendlock = semaphore.Semaphore()
@staticmethod
def pack_message(message):
def _pack_message(message):
"""Pack the message inside ``00`` and ``FF``
As per the dataframing section (5.3) for the websocket spec
@@ -87,11 +103,10 @@ class WebSocket(object):
packed = "\x00%s\xFF" % message
return packed
def parse_messages(self):
def _parse_messages(self):
""" Parses for messages in the buffer *buf*. It is assumed that
the buffer contains the start character for a message, but that it
may contain only part of the rest of the message. NOTE: only understands
lengthless messages for now.
may contain only part of the rest of the message.
Returns an array of messages, and the buffer remainder that
didn't contain any full messages."""
@@ -109,10 +124,10 @@ class WebSocket(object):
return msgs
def send(self, message):
"""Send a message to the client. *message* should be
"""Send a message to the browser. *message* should be
convertable to a string; unicode objects should be encodable
as utf-8."""
packed = self.pack_message(message)
packed = self._pack_message(message)
# if two greenthreads are trying to send at the same time
# on the same socket, sendlock prevents interleaving and corruption
self._sendlock.acquire()
@@ -130,7 +145,7 @@ class WebSocket(object):
if delta == '':
return None
self._buf += delta
msgs = self.parse_messages()
msgs = self._parse_messages()
self._msgs.extend(msgs)
return self._msgs.popleft()

View File

@@ -5,6 +5,8 @@ from eventlet import websocket
# demo app
import os
import random
@websocket.WebSocketWSGI
def handle(ws):
""" This is the websocket handler function. Note that we
can dispatch based on path in here, too."""
@@ -20,12 +22,11 @@ def handle(ws):
ws.send("0 %s %s\n" % (i, random.random()))
eventlet.sleep(0.1)
wsapp = websocket.WebSocketWSGI(handle)
def dispatch(environ, start_response):
""" This resolves to the web page or the websocket depending on
the path."""
if environ['PATH_INFO'] == '/data':
return wsapp(environ, start_response)
return handle(environ, start_response)
else:
start_response('200 OK', [('content-type', 'text/html')])
return [open(os.path.join(