11Getting Started
22===============
33
4+ .. currentmodule :: wsproto
5+
46This document explains how to get started using wsproto to connect to
57WebSocket servers as well as how to write your own.
68
@@ -13,93 +15,82 @@ behind writing a network protocol library that doesn't do any network I/O.
1315Connections
1416-----------
1517
16- The main class you'll be working with is the
17- :class: `WSConnection <wsproto.WSConnection> ` object. This object
18- represents a connection to a WebSocket client or server and contains all the
19- state needed to communicate with the entity at the other end. Whether you're
20- connecting to a server or receiving a connection from a client, this is the
21- object you'll use.
18+ The main class you'll be working with is the :class: `WSConnection ` object. This
19+ object represents a connection to a WebSocket peer. This class can handle both
20+ WebSocket clients and WebSocket servers.
2221
23- `wsproto ` provides two layers of abstractions. You need to write code that
22+ `` wsproto ` ` provides two layers of abstractions. You need to write code that
2423interfaces with both of these layers. The following diagram illustrates how your
25- code is like a sandwich around `wsproto `.
26-
27- +--------------------+
28- | Application |
29- +--------------------+
30- | < APPLICATION GLUE> |
31- +--------------------+
32- | wsproto |
33- +--------------------+
34- | < NETWORK GLUE> |
35- +--------------------+
36- | Network Layer |
37- +--------------------+
38-
39- `wsproto ` does not do perform any network I/O, so `` < NETWORK GLUE> ``
40- represents the code you need to write to glue `wsproto ` to the actual
41- network layer, i.e. code that can send and receive data over the
42- network. The :class: `WSConnection <wsproto.WSConnection> `
43- class provides two methods for this purpose. When data has been
44- received on a network socket, you feed this data into ` wsproto ` by
45- calling :meth: ` receive_data
46- <wsproto.WSConnection.receive_data> `. When ` wsproto ` sends
47- events the :meth: ` send < wsproto.WSConnection.send> ` will
48- return the bytes that need to be sent over the network. Your code is
49- responsible for actually sending that data over the network.
24+ code is like a sandwich around `` wsproto ` `.
25+
26+ +---------------------- +
27+ | Application |
28+ +---------------------- +
29+ | ** APPLICATION GLUE ** |
30+ +---------------------- +
31+ | wsproto |
32+ +---------------------- +
33+ | ** NETWORK GLUE ** |
34+ +---------------------- +
35+ | Network Layer |
36+ +---------------------- +
37+
38+ `` wsproto `` does not do perform any network I/O, so ** NETWORK GLUE ** represents
39+ the code you need to write to glue `` wsproto `` to an actual network, for example
40+ using Python's ` socket < https://docs.python.org/3/library/socket.html >`_ module.
41+ The :class: `WSConnection ` class provides two methods for this purpose. When data
42+ has been received on a network socket, you should feed this data into a
43+ connection instance by calling :meth: ` WSConnection.receive_data `. When you want
44+ to communicate with the remote peer, e.g. send a message, ping, or close the
45+ connection, you should create an instance of one of the
46+ :class: ` wsproto.events.Event ` subclasses and pass it to
47+ :meth: ` WSConnection.send ` to get the corresponding bytes that need to be sent.
48+ Your code is responsible for actually sending that data over the network.
5049
5150.. note ::
5251
5352 If the connection drops, a standard Python ``socket.recv() `` will return
54- zero. You should call ``receive_data(None) `` to update the internal
55- `wsproto ` state to indicate that the connection has been closed.
56-
57- Internally, `wsproto ` process the raw network data you feed into it and turns it
58- into higher level representations of WebSocket events. In ``<APPLICATION
59- GLUE> ``, you need to write code to process these events. The
60- :class: `WSConnection <wsproto.WSConnection> ` class contains a
61- generator method :meth: `events <wsproto.WSConnection.events> ` that
62- yields WebSocket events. To send a message, you call the :meth: `send
63- <wsproto.WSConnection.send> ` method.
64-
65- Connecting to a WebSocket server
66- --------------------------------
67-
68- Begin by instantiating a connection object in the client mode and then
69- create a :class: `Request <wsproto.events.Request> ` instance to
70- send. The Request must specify ``host `` and ``target `` arguments. If
71- the WebSocket server is located at ``http://example.com/foo ``, then you
72- would instantiate the connection as follows::
53+ zero bytes. You should call ``receive_data(None) `` to update the internal
54+ ``wsproto `` state to indicate that the connection has been closed.
55+
56+ Internally, ``wsproto `` processes the raw network data you feed into it and
57+ turns it into higher level representations of WebSocket events. In **APPLICATION
58+ GLUE **, you need to write code to process these events. Incoming data is exposed
59+ though the generator method :meth: `WSConnection.events `, which yields WebSocket
60+ events. Each event is an instance of an :class: `.events.Event ` subclass.
61+
62+ WebSocket Clients
63+ -----------------
7364
65+ Begin by instantiating a connection object in client mode and then create a
66+ :class: `wsproto.events.Request ` instance. The Request must specify ``host `` and
67+ ``target `` arguments. If the WebSocket server is located at
68+ ``http://example.com/foo ``, then you would instantiate the connection as
69+ follows::
70+
71+ from wsproto import ConnectionType, WSConnection
72+ from wsproto.events import Request
7473 ws = WSConnection(ConnectionType.CLIENT)
75- ws.send(Request(host="example.com", target='foo'))
74+ request = Request(host="example.com", target='foo')
75+ data = ws.send(request)
7676
77- Now you need to provide the network glue. For the sake of example, we will use
78- standard Python sockets here, but ` wsproto ` can be integrated with any network
79- layer ::
77+ Keep in mind that `` wsproto `` does not do any network I/O. Instead,
78+ :meth: ` WSConnection.send ` returns data that you must send to the remote peer.
79+ Here is an example using a standard Python socket ::
8080
8181 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
8282 sock.connect(("example.com", 80))
83+ sock.send(data)
8384
84- To read from the network::
85+ To receive communications from the peer, you must pass the data received from
86+ the peer into the connection instance::
8587
8688 data = sock.recv(4096)
8789 ws.receive_data(data)
8890
89- You also need to send data returned by the send method::
90-
91- data = ws.send(Message(data=b"Hello"))
92- sock.send(data)
93-
94- A standard Python socket will block on the call to ``sock.recv() ``, so you
95- will probably need to use a non-blocking socket or some form of concurrency like
96- threading, greenlets, asyncio, etc.
97-
98- You also need to provide the application glue. To send a WebSocket message::
99-
100- ws.send(Message(data="Hello world!"))
101-
102- And to receive WebSocket events::
91+ The connection instance parses the received data and determines if any high-level
92+ events have occurred, such as receiving a ping or a message. To retrieve these
93+ events, use the generator function :meth: `WSConnection.events `::
10394
10495 for event in ws.events():
10596 if isinstance(event, AcceptConnection):
@@ -126,7 +117,7 @@ And to receive WebSocket events::
126117 print('Unknown event: {!r}'.format(event))
127118
128119The method ``events() `` returns a generator which will yield events for all of
129- the data currently in the `wsproto ` internal buffer and then exit. Therefore,
120+ the data currently in the `` wsproto ` ` internal buffer and then exit. Therefore,
130121you should iterate over this generator after receiving new network data.
131122
132123For a more complete example, see `synchronous_client.py
@@ -135,9 +126,13 @@ For a more complete example, see `synchronous_client.py
135126WebSocket Servers
136127-----------------
137128
138- A WebSocket server is similar to a client except that it uses a different
139- constant::
129+ A WebSocket server is similar to a client, but it uses a different
130+ :class: ` wsproto.ConnectionType ` constant.
140131
132+ ::
133+
134+ from wsproto import ConnectionType, WSConnection
135+ from wsproto.events import Request
141136 ws = WSConnection(ConnectionType.SERVER)
142137
143138A server also needs to explicitly send an :class: `AcceptConnection
@@ -148,24 +143,7 @@ A server also needs to explicitly send an :class:`AcceptConnection
148143 if isinstance(event, Request):
149144 print('Accepting connection request')
150145 sock.send(ws.send(AcceptConnection()))
151- elif isinstance(event, CloseConnection):
152- print('Connection closed: code={} reason={}'.format(
153- event.code, event.reason
154- ))
155- sock.send(ws.send(event.response()))
156- elif isinstance(event, Ping):
157- print('Received Ping frame with payload {}'.format(event.payload))
158- sock.send(ws.send(event.response()))
159- elif isinstance(event, TextMessage):
160- print('Received TEXT data: {}'.format(event.data))
161- if event.message_finished:
162- print('TEXT Message finished.')
163- elif isinstance(event, BinaryMessage):
164- print('Received BINARY data: {}'.format(event.data))
165- if event.message_finished:
166- print('BINARY Message finished.')
167- else:
168- print('Unknown event: {!r}'.format(event))
146+ elif...
169147
170148Alternatively a server can explicitly reject the connection by sending
171149:class: `RejectConnection <wsproto.events.RejectConnection> ` after
@@ -193,9 +171,9 @@ send one frame and receive one frame. Sending a
193171:class: `CloseConnection <wsproto.events.CloseConnection> ` instance
194172sets the state to ``LOCAL_CLOSING ``. When a close frame is received,
195173it yields a ``CloseConnection `` event, sets the state to
196- ``REMOTE_CLOSING `` **and requires a reply to be sent **, this reply
174+ ``REMOTE_CLOSING `` **and requires a reply to be sent **. This reply
197175should be a ``CloseConnection `` event. To aid with this the
198- ``CloseConnection `` class has a :func : `response()
176+ ``CloseConnection `` class has a :meth : `response()
199177<wsproto.events.CloseConnection.response> ` method to create the
200178appropriate reply. For example,
201179
@@ -207,11 +185,10 @@ appropriate reply. For example,
207185 When the reply has been received by the initiator, it will also yield
208186a ``CloseConnection `` event.
209187
210- Regardless of which endpoint initiates the closing handshake, the
211- server is responsible for tearing down the underlying connection. When
212- the server receives a ``CloseConnection `` event, it should send
213- pending `wsproto ` data (if any) and then it can start tearing down the
214- underlying connection.
188+ Regardless of which endpoint initiates the closing handshake, the server is
189+ responsible for tearing down the underlying connection. When a
190+ ``CloseConnection `` event is generated, it should send pending any ``wsproto ``
191+ data and then tear down the underlying connection.
215192
216193.. note ::
217194
@@ -226,7 +203,7 @@ sending WebSocket ping and pong frames via sending :class:`Ping
226203<wsproto.events.Ping> ` and :class: `Pong <wsproto.events.Pong> `. When a
227204``Ping `` frame is received it **requires a reply **, this reply should be
228205a ``Pong `` event. To aid with this the ``Ping `` class has a
229- :func : `response() <wsproto.events.Ping.response> ` method to create the
206+ :meth : `response() <wsproto.events.Ping.response> ` method to create the
230207appropriate reply. For example,
231208
232209.. code-block :: python
0 commit comments