1
# Copyright 2011, Google Inc.
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions are
8
# * Redistributions of source code must retain the above copyright
9
# notice, this list of conditions and the following disclaimer.
10
# * Redistributions in binary form must reproduce the above
11
# copyright notice, this list of conditions and the following disclaimer
12
# in the documentation and/or other materials provided with the
14
# * Neither the name of Google Inc. nor the names of its
15
# contributors may be used to endorse or promote products derived from
16
# this software without specific prior written permission.
18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
"""Message related utilities.
33
Note: request.connection.write/read are used in this module, even though
34
mod_python document says that they should be used only in connection
35
handlers. Unfortunately, we have no other options. For example,
36
request.write/read are not suitable because they don't allow direct raw
37
bytes writing/reading.
45
# Export Exception symbols from msgutil for backward compatibility
46
from mod_pywebsocket._stream_base import ConnectionTerminatedException
47
from mod_pywebsocket._stream_base import InvalidFrameException
48
from mod_pywebsocket._stream_base import BadOperationException
49
from mod_pywebsocket._stream_base import UnsupportedFrameException
52
# An API for handler to send/receive WebSocket messages.
53
def close_connection(request):
57
request: mod_python request.
59
request.ws_stream.close_connection()
62
def send_message(request, payload_data, end=True, binary=False):
63
"""Send a message (or part of a message).
66
request: mod_python request.
67
payload_data: unicode text or str binary to send.
68
end: True to terminate a message.
69
False to send payload_data as part of a message that is to be
70
terminated by next or later send_message call with end=True.
71
binary: send payload_data as binary frame(s).
73
BadOperationException: when server already terminated.
75
request.ws_stream.send_message(payload_data, end, binary)
78
def receive_message(request):
79
"""Receive a WebSocket frame and return its payload as a text in
80
unicode or a binary in str.
83
request: mod_python request.
85
InvalidFrameException: when client send invalid frame.
86
UnsupportedFrameException: when client send unsupported frame e.g. some
87
of reserved bit is set but no extension can
89
InvalidUTF8Exception: when client send a text frame containing any
91
ConnectionTerminatedException: when the connection is closed
93
BadOperationException: when client already terminated.
95
return request.ws_stream.receive_message()
98
def send_ping(request, body=''):
99
request.ws_stream.send_ping(body)
102
class MessageReceiver(threading.Thread):
103
"""This class receives messages from the client.
105
This class provides three ways to receive messages: blocking,
106
non-blocking, and via callback. Callback has the highest precedence.
108
Note: This class should not be used with the standalone server for wss
109
because pyOpenSSL used by the server raises a fatal error if the socket
110
is accessed from multiple threads.
113
def __init__(self, request, onmessage=None):
114
"""Construct an instance.
117
request: mod_python request.
118
onmessage: a function to be called when a message is received.
119
May be None. If not None, the function is called on
120
another thread. In that case, MessageReceiver.receive
121
and MessageReceiver.receive_nowait are useless
122
because they will never return any messages.
125
threading.Thread.__init__(self)
126
self._request = request
127
self._queue = Queue.Queue()
128
self._onmessage = onmessage
129
self._stop_requested = False
135
while not self._stop_requested:
136
message = receive_message(self._request)
138
self._onmessage(message)
140
self._queue.put(message)
142
close_connection(self._request)
145
""" Receive a message from the channel, blocking.
148
message as a unicode string.
150
return self._queue.get()
152
def receive_nowait(self):
153
""" Receive a message from the channel, non-blocking.
156
message as a unicode string if available. None otherwise.
159
message = self._queue.get_nowait()
165
"""Request to stop this instance.
167
The instance will be stopped after receiving the next message.
168
This method may not be very useful, but there is no clean way
169
in Python to forcefully stop a running thread.
171
self._stop_requested = True
174
class MessageSender(threading.Thread):
175
"""This class sends messages to the client.
177
This class provides both synchronous and asynchronous ways to send
180
Note: This class should not be used with the standalone server for wss
181
because pyOpenSSL used by the server raises a fatal error if the socket
182
is accessed from multiple threads.
185
def __init__(self, request):
186
"""Construct an instance.
189
request: mod_python request.
191
threading.Thread.__init__(self)
192
self._request = request
193
self._queue = Queue.Queue()
199
message, condition = self._queue.get()
201
send_message(self._request, message)
205
def send(self, message):
206
"""Send a message, blocking."""
208
condition = threading.Condition()
210
self._queue.put((message, condition))
213
def send_nowait(self, message):
214
"""Send a message, non-blocking."""
216
self._queue.put((message, threading.Condition()))