1
1
Getting Started
2
2
===============
3
3
4
+ .. currentmodule :: wsproto
5
+
4
6
This document explains how to get started using wsproto to connect to
5
7
WebSocket servers as well as how to write your own.
6
8
@@ -13,93 +15,82 @@ behind writing a network protocol library that doesn't do any network I/O.
13
15
Connections
14
16
-----------
15
17
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.
22
21
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
24
23
interfaces 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.
50
49
51
50
.. note ::
52
51
53
52
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
+ -----------------
73
64
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
74
73
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)
76
76
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 ::
80
80
81
81
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
82
82
sock.connect(("example.com", 80))
83
+ sock.send(data)
83
84
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::
85
87
86
88
data = sock.recv(4096)
87
89
ws.receive_data(data)
88
90
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 `::
103
94
104
95
for event in ws.events():
105
96
if isinstance(event, AcceptConnection):
@@ -126,7 +117,7 @@ And to receive WebSocket events::
126
117
print('Unknown event: {!r}'.format(event))
127
118
128
119
The 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,
130
121
you should iterate over this generator after receiving new network data.
131
122
132
123
For a more complete example, see `synchronous_client.py
@@ -135,9 +126,13 @@ For a more complete example, see `synchronous_client.py
135
126
WebSocket Servers
136
127
-----------------
137
128
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.
140
131
132
+ ::
133
+
134
+ from wsproto import ConnectionType, WSConnection
135
+ from wsproto.events import Request
141
136
ws = WSConnection(ConnectionType.SERVER)
142
137
143
138
A server also needs to explicitly send an :class: `AcceptConnection
@@ -148,24 +143,7 @@ A server also needs to explicitly send an :class:`AcceptConnection
148
143
if isinstance(event, Request):
149
144
print('Accepting connection request')
150
145
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...
169
147
170
148
Alternatively a server can explicitly reject the connection by sending
171
149
:class: `RejectConnection <wsproto.events.RejectConnection> ` after
@@ -193,9 +171,9 @@ send one frame and receive one frame. Sending a
193
171
:class: `CloseConnection <wsproto.events.CloseConnection> ` instance
194
172
sets the state to ``LOCAL_CLOSING ``. When a close frame is received,
195
173
it 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
197
175
should be a ``CloseConnection `` event. To aid with this the
198
- ``CloseConnection `` class has a :func : `response()
176
+ ``CloseConnection `` class has a :meth : `response()
199
177
<wsproto.events.CloseConnection.response> ` method to create the
200
178
appropriate reply. For example,
201
179
@@ -207,11 +185,10 @@ appropriate reply. For example,
207
185
When the reply has been received by the initiator, it will also yield
208
186
a ``CloseConnection `` event.
209
187
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.
215
192
216
193
.. note ::
217
194
@@ -226,7 +203,7 @@ sending WebSocket ping and pong frames via sending :class:`Ping
226
203
<wsproto.events.Ping> ` and :class: `Pong <wsproto.events.Pong> `. When a
227
204
``Ping `` frame is received it **requires a reply **, this reply should be
228
205
a ``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
230
207
appropriate reply. For example,
231
208
232
209
.. code-block :: python
0 commit comments