Implementation of web sockets for Python, upgrades a regular socket to a web socket

Taddeus Kroes 7c99e4fe3a Split extension.py into base file and extension implementation files há 12 anos atrás
test 7c99e4fe3a Split extension.py into base file and extension implementation files há 12 anos atrás
.gitignore 6d2cf25d8d Added SSL support (wss://...), updated some docs há 12 anos atrás
LICENSE d758ca45b0 Added license há 13 anos atrás
Makefile 8f15e28308 Implemented client handshake, and did some corresponding debugging há 12 anos atrás
README.md 5e9b432e06 Changed Server arguments 'hostname' and 'port' to an 'address' tuple for consistency há 12 anos atrás
TODO 7d246d5dba Implemented extensions in handshakes and installed extension hooks in websocket há 12 anos atrás
__init__.py 7c99e4fe3a Split extension.py into base file and extension implementation files há 12 anos atrás
connection.py 0c45c1c435 Implemented connection hooks, cleaned up some code há 12 anos atrás
deflate_frame.py 7c99e4fe3a Split extension.py into base file and extension implementation files há 12 anos atrás
errors.py 6d2cf25d8d Added SSL support (wss://...), updated some docs há 12 anos atrás
extension.py 7c99e4fe3a Split extension.py into base file and extension implementation files há 12 anos atrás
frame.py 80538863f6 Fixed issue where server crashes when client closes connection há 12 anos atrás
handshake.py 2963b26e5d Added DeflateFrame to test server, some bugfixes to fully enable extensions há 12 anos atrás
message.py a400500e23 Code cleanup há 12 anos atrás
multiplex.py 7c99e4fe3a Split extension.py into base file and extension implementation files há 12 anos atrás
python_digest.py ee818ad017 Updated external copyright header with download link há 12 anos atrás
server.py 4388fc5c65 Minor bugfixes há 12 anos atrás
setup.py c1bcd85291 Added setup script há 12 anos atrás
websocket.py 6ec710a518 Extensions are now passed to websocket.accept() properly, fixed origin/location checking há 12 anos atrás

README.md

About

twspy is a standalone implementation of web sockets for Python, defined by RFC 6455. The incentive for creating this library is the absence of a layered implementation of web sockets outside the scope of web servers such as Apache or Nginx. twspy does not require any third-party programs or libraries outside Python's standard library. It provides low-level access to sockets, as well as high-level functionalities to easily set up a web server. Thus, it is both suited for quick server programming, as well as for more demanding applications that require low-level control over each frame being sent/received.

Her is a quick overview of the features in this library:

  • Upgrading regular sockets to web sockets.
  • Building custom frames.
  • Messages, which are higher-level than frames (see "Basic usage").
  • Connections, which hide the handling of control frames and automatically concatenate fragmented messages to individual payloads.
  • HTTP authentication during handshake.
  • An extendible server implementation.
  • Secure sockets using SSL certificates (for 'wss://...' URL's).
  • The possibility to add extensions to the web socket protocol. An included implementation is deflate-frame.

Basic usage

  • The websocket class upgrades a regular socket to a web socket. A websocket instance is a single end point of a connection. A websocket instance sends and receives frames (Frame instances) as opposed to bytes (which are sent/received in a regular socket).

Server example:

    import twspy, socket
    sock = twspy.websocket()
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('', 8000))
    sock.listen()

    client = sock.accept()
    client.send(twspy.Frame(twspy.OPCODE_TEXT, 'Hello, Client!'))
    frame = client.recv()

Client example:

    import twspy
    sock = twspy.websocket(location='/my/path')
    sock.connect(('', 8000))
    sock.send(twspy.Frame(twspy.OPCODE_TEXT, 'Hello, Server!'))
  • A Connection instance represents a connection between two end points, based on a websocket instance. A connection handles control frames properly, and sends/receives messages (Message instances, which are higher-level than frames). Messages are automatically converted to frames, and received frames are converted to messages. Fragmented messages (messages consisting of multiple frames) are also supported.

Example of an echo server (sends back what it receives):

    import twspy

    class EchoConnection(twspy.Connection):
        def onopen(self):
            print 'Connection opened at %s:%d' % self.sock.getpeername()

        def onmessage(self, message):
            print 'Received message "%s"' % message.payload
            self.send(twspy.TextMessage(message.payload))

        def onclose(self, message):
            print 'Connection closed'

    server = twspy.websocket()
    server.bind(('', 8000))
    server.listen()

    while True:
        client, addr = server.accept()
        EchoConnection(client).receive_forever()

There are two types of messages: TextMessages and BinaryMessages. A TextMessage uses frames with opcode OPCODE_TEXT, and encodes its payload using UTF-8 encoding. A BinaryMessage just sends its payload as raw data. I recommend using TextMessage by default, and BinaryMessage only when necessary.

Note: For browser clients, you will probably want to use JSON encoding. This could, for example, be implemented as follows:

    import twspy, json

    def msg(**data):
        return twspy.TextMessage(json.dumps(data))

    # create some connection `conn`...

    conn.send(msg(foo='Hello, World!'))
  • The built-in Server implementation is very basic. It starts a new thread with a Connection.receive_forever() loop for each client that connects. It also handles client crashes properly. By default, a Server instance only logs every event using Python's logging module. To create a custom server, The Server class should be extended and its event handlers overwritten. The event handlers are named identically to the Connection event handlers, but they also receive an additional client argument. This argument is a modified Connection instance, so you can invoke send() and recv().

For example, the EchoConnection example above can be rewritten to:

    import twspy

    class EchoServer(twspy.Server):
        def onopen(self, client):
            print 'Client %s connected' % client

        def onmessage(self, client, message):
            print 'Received message "%s"' % message.payload
            client.send(twspy.TextMessage(message.payload))

        def onclose(self, client):
            print 'Client %s disconnected' % client

    EchoServer(('', 8000)).run()

The server can be stopped by typing CTRL-C in the command line. The KeyboardInterrupt raised when this happens is caught by the server.

Extensions

TODO