Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 # Copyright 2011, Google Inc.
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
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
13 # distribution.
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.
17 #
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 """This file provides a class for parsing/building frames of the WebSocket
32 protocol version HyBi 00 and Hixie 75.
34 Specification:
35 http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
36 """
39 from mod_pywebsocket import common
40 from mod_pywebsocket._stream_base import BadOperationException
41 from mod_pywebsocket._stream_base import ConnectionTerminatedException
42 from mod_pywebsocket._stream_base import InvalidFrameException
43 from mod_pywebsocket._stream_base import StreamBase
44 from mod_pywebsocket._stream_base import UnsupportedFrameException
45 from mod_pywebsocket import util
48 class StreamHixie75(StreamBase):
49 """A class for parsing/building frames of the WebSocket protocol version
50 HyBi 00 and Hixie 75.
51 """
53 def __init__(self, request, enable_closing_handshake=False):
54 """Construct an instance.
56 Args:
57 request: mod_python request.
58 enable_closing_handshake: to let StreamHixie75 perform closing
59 handshake as specified in HyBi 00, set
60 this option to True.
61 """
63 StreamBase.__init__(self, request)
65 self._logger = util.get_class_logger(self)
67 self._enable_closing_handshake = enable_closing_handshake
69 self._request.client_terminated = False
70 self._request.server_terminated = False
72 def send_message(self, message, end=True, binary=False):
73 """Send message.
75 Args:
76 message: unicode string to send.
77 binary: not used in hixie75.
79 Raises:
80 BadOperationException: when called on a server-terminated
81 connection.
82 """
84 if not end:
85 raise BadOperationException(
86 'StreamHixie75 doesn\'t support send_message with end=False')
88 if binary:
89 raise BadOperationException(
90 'StreamHixie75 doesn\'t support send_message with binary=True')
92 if self._request.server_terminated:
93 raise BadOperationException(
94 'Requested send_message after sending out a closing handshake')
96 self._write(''.join(['\x00', message.encode('utf-8'), '\xff']))
98 def _read_payload_length_hixie75(self):
99 """Reads a length header in a Hixie75 version frame with length.
101 Raises:
102 ConnectionTerminatedException: when read returns empty string.
103 """
105 length = 0
106 while True:
107 b_str = self._read(1)
108 b = ord(b_str)
109 length = length * 128 + (b & 0x7f)
110 if (b & 0x80) == 0:
111 break
112 return length
114 def receive_message(self):
115 """Receive a WebSocket frame and return its payload an unicode string.
117 Returns:
118 payload unicode string in a WebSocket frame.
120 Raises:
121 ConnectionTerminatedException: when read returns empty
122 string.
123 BadOperationException: when called on a client-terminated
124 connection.
125 """
127 if self._request.client_terminated:
128 raise BadOperationException(
129 'Requested receive_message after receiving a closing '
130 'handshake')
132 while True:
133 # Read 1 byte.
134 # mp_conn.read will block if no bytes are available.
135 # Timeout is controlled by TimeOut directive of Apache.
136 frame_type_str = self.receive_bytes(1)
137 frame_type = ord(frame_type_str)
138 if (frame_type & 0x80) == 0x80:
139 # The payload length is specified in the frame.
140 # Read and discard.
141 length = self._read_payload_length_hixie75()
142 if length > 0:
143 _ = self.receive_bytes(length)
144 # 5.3 3. 12. if /type/ is 0xFF and /length/ is 0, then set the
145 # /client terminated/ flag and abort these steps.
146 if not self._enable_closing_handshake:
147 continue
149 if frame_type == 0xFF and length == 0:
150 self._request.client_terminated = True
152 if self._request.server_terminated:
153 self._logger.debug(
154 'Received ack for server-initiated closing '
155 'handshake')
156 return None
158 self._logger.debug(
159 'Received client-initiated closing handshake')
161 self._send_closing_handshake()
162 self._logger.debug(
163 'Sent ack for client-initiated closing handshake')
164 return None
165 else:
166 # The payload is delimited with \xff.
167 bytes = self._read_until('\xff')
168 # The WebSocket protocol section 4.4 specifies that invalid
169 # characters must be replaced with U+fffd REPLACEMENT
170 # CHARACTER.
171 message = bytes.decode('utf-8', 'replace')
172 if frame_type == 0x00:
173 return message
174 # Discard data of other types.
176 def _send_closing_handshake(self):
177 if not self._enable_closing_handshake:
178 raise BadOperationException(
179 'Closing handshake is not supported in Hixie 75 protocol')
181 self._request.server_terminated = True
183 # 5.3 the server may decide to terminate the WebSocket connection by
184 # running through the following steps:
185 # 1. send a 0xFF byte and a 0x00 byte to the client to indicate the
186 # start of the closing handshake.
187 self._write('\xff\x00')
189 def close_connection(self, unused_code='', unused_reason=''):
190 """Closes a WebSocket connection.
192 Raises:
193 ConnectionTerminatedException: when closing handshake was
194 not successfull.
195 """
197 if self._request.server_terminated:
198 self._logger.debug(
199 'Requested close_connection but server is already terminated')
200 return
202 if not self._enable_closing_handshake:
203 self._request.server_terminated = True
204 self._logger.debug('Connection closed')
205 return
207 self._send_closing_handshake()
208 self._logger.debug('Sent server-initiated closing handshake')
210 # TODO(ukai): 2. wait until the /client terminated/ flag has been set,
211 # or until a server-defined timeout expires.
212 #
213 # For now, we expect receiving closing handshake right after sending
214 # out closing handshake, and if we couldn't receive non-handshake
215 # frame, we take it as ConnectionTerminatedException.
216 message = self.receive_message()
217 if message is not None:
218 raise ConnectionTerminatedException(
219 'Didn\'t receive valid ack for closing handshake')
220 # TODO: 3. close the WebSocket connection.
221 # note: mod_python Connection (mp_conn) doesn't have close method.
223 def send_ping(self, body):
224 raise BadOperationException(
225 'StreamHixie75 doesn\'t support send_ping')
228 # vi:sts=4 sw=4 et