Added documentation for websocket module and switched to using decorators in the examples.
This commit is contained in:
@@ -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.
|
This is an example implementation of the producer/consumer pattern as well as a functional recursive web crawler.
|
||||||
|
|
||||||
.. literalinclude:: ../examples/producer_consumer.py
|
.. 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
|
||||||
|
@@ -15,4 +15,5 @@ Module Reference
|
|||||||
modules/queue
|
modules/queue
|
||||||
modules/semaphore
|
modules/semaphore
|
||||||
modules/timeout
|
modules/timeout
|
||||||
|
modules/websocket
|
||||||
modules/wsgi
|
modules/wsgi
|
||||||
|
@@ -9,8 +9,22 @@ from eventlet.support import get_errno
|
|||||||
|
|
||||||
ACCEPTABLE_CLIENT_ERRORS = set((errno.ECONNRESET, errno.EPIPE))
|
ACCEPTABLE_CLIENT_ERRORS = set((errno.ECONNRESET, errno.EPIPE))
|
||||||
|
|
||||||
|
__all__ = ["WebSocketWSGI", "WebSocket"]
|
||||||
|
|
||||||
class WebSocketWSGI(object):
|
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):
|
def __init__(self, handler):
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
@@ -29,9 +43,9 @@ class WebSocketWSGI(object):
|
|||||||
"Connection: Upgrade\r\n"
|
"Connection: Upgrade\r\n"
|
||||||
"WebSocket-Origin: %s\r\n"
|
"WebSocket-Origin: %s\r\n"
|
||||||
"WebSocket-Location: ws://%s%s\r\n\r\n" % (
|
"WebSocket-Location: ws://%s%s\r\n\r\n" % (
|
||||||
environ.get('HTTP_ORIGIN'),
|
environ.get('HTTP_ORIGIN'),
|
||||||
environ.get('HTTP_HOST'),
|
environ.get('HTTP_HOST'),
|
||||||
environ.get('PATH_INFO')))
|
environ.get('PATH_INFO')))
|
||||||
sock.sendall(handshake_reply)
|
sock.sendall(handshake_reply)
|
||||||
try:
|
try:
|
||||||
self.handler(ws)
|
self.handler(ws)
|
||||||
@@ -42,22 +56,24 @@ class WebSocketWSGI(object):
|
|||||||
# doesn't barf on the fact that we didn't call start_response
|
# doesn't barf on the fact that we didn't call start_response
|
||||||
return wsgi.ALREADY_HANDLED
|
return wsgi.ALREADY_HANDLED
|
||||||
|
|
||||||
|
|
||||||
class WebSocket(object):
|
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
|
The primary way to interact with a :class:`WebSocket` object is to
|
||||||
:meth:`send` and :meth:`wait` in order to pass messages back and
|
call :meth:`send` and :meth:`wait` in order to pass messages back
|
||||||
forth with the client. Also available are the following properties:
|
and forth with the browser. Also available are the following
|
||||||
|
properties:
|
||||||
|
|
||||||
path
|
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
|
protocol
|
||||||
The value of the Websocket-Protocol header.
|
The value of the Websocket-Protocol header.
|
||||||
origin
|
origin
|
||||||
The value of the 'Origin' header.
|
The value of the 'Origin' header.
|
||||||
environ
|
environ
|
||||||
The full WSGI environment for this request.
|
The full WSGI environment for this request.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, sock, environ):
|
def __init__(self, sock, environ):
|
||||||
"""
|
"""
|
||||||
@@ -75,7 +91,7 @@ class WebSocket(object):
|
|||||||
self._sendlock = semaphore.Semaphore()
|
self._sendlock = semaphore.Semaphore()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pack_message(message):
|
def _pack_message(message):
|
||||||
"""Pack the message inside ``00`` and ``FF``
|
"""Pack the message inside ``00`` and ``FF``
|
||||||
|
|
||||||
As per the dataframing section (5.3) for the websocket spec
|
As per the dataframing section (5.3) for the websocket spec
|
||||||
@@ -87,11 +103,10 @@ class WebSocket(object):
|
|||||||
packed = "\x00%s\xFF" % message
|
packed = "\x00%s\xFF" % message
|
||||||
return packed
|
return packed
|
||||||
|
|
||||||
def parse_messages(self):
|
def _parse_messages(self):
|
||||||
""" Parses for messages in the buffer *buf*. It is assumed that
|
""" Parses for messages in the buffer *buf*. It is assumed that
|
||||||
the buffer contains the start character for a message, but that it
|
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
|
may contain only part of the rest of the message.
|
||||||
lengthless messages for now.
|
|
||||||
|
|
||||||
Returns an array of messages, and the buffer remainder that
|
Returns an array of messages, and the buffer remainder that
|
||||||
didn't contain any full messages."""
|
didn't contain any full messages."""
|
||||||
@@ -109,10 +124,10 @@ class WebSocket(object):
|
|||||||
return msgs
|
return msgs
|
||||||
|
|
||||||
def send(self, message):
|
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
|
convertable to a string; unicode objects should be encodable
|
||||||
as utf-8."""
|
as utf-8."""
|
||||||
packed = self.pack_message(message)
|
packed = self._pack_message(message)
|
||||||
# if two greenthreads are trying to send at the same time
|
# if two greenthreads are trying to send at the same time
|
||||||
# on the same socket, sendlock prevents interleaving and corruption
|
# on the same socket, sendlock prevents interleaving and corruption
|
||||||
self._sendlock.acquire()
|
self._sendlock.acquire()
|
||||||
@@ -130,7 +145,7 @@ class WebSocket(object):
|
|||||||
if delta == '':
|
if delta == '':
|
||||||
return None
|
return None
|
||||||
self._buf += delta
|
self._buf += delta
|
||||||
msgs = self.parse_messages()
|
msgs = self._parse_messages()
|
||||||
self._msgs.extend(msgs)
|
self._msgs.extend(msgs)
|
||||||
return self._msgs.popleft()
|
return self._msgs.popleft()
|
||||||
|
|
||||||
|
@@ -5,6 +5,8 @@ from eventlet import websocket
|
|||||||
# demo app
|
# demo app
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
@websocket.WebSocketWSGI
|
||||||
def handle(ws):
|
def handle(ws):
|
||||||
""" This is the websocket handler function. Note that we
|
""" This is the websocket handler function. Note that we
|
||||||
can dispatch based on path in here, too."""
|
can dispatch based on path in here, too."""
|
||||||
@@ -20,12 +22,11 @@ def handle(ws):
|
|||||||
ws.send("0 %s %s\n" % (i, random.random()))
|
ws.send("0 %s %s\n" % (i, random.random()))
|
||||||
eventlet.sleep(0.1)
|
eventlet.sleep(0.1)
|
||||||
|
|
||||||
wsapp = websocket.WebSocketWSGI(handle)
|
|
||||||
def dispatch(environ, start_response):
|
def dispatch(environ, start_response):
|
||||||
""" This resolves to the web page or the websocket depending on
|
""" This resolves to the web page or the websocket depending on
|
||||||
the path."""
|
the path."""
|
||||||
if environ['PATH_INFO'] == '/data':
|
if environ['PATH_INFO'] == '/data':
|
||||||
return wsapp(environ, start_response)
|
return handle(environ, start_response)
|
||||||
else:
|
else:
|
||||||
start_response('200 OK', [('content-type', 'text/html')])
|
start_response('200 OK', [('content-type', 'text/html')])
|
||||||
return [open(os.path.join(
|
return [open(os.path.join(
|
||||||
|
Reference in New Issue
Block a user