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. 29 30 31 """Base stream class. 32 """ 33 34 35 # Note: request.connection.write/read are used in this module, even though 36 # mod_python document says that they should be used only in connection 37 # handlers. Unfortunately, we have no other options. For example, 38 # request.write/read are not suitable because they don't allow direct raw bytes 39 # writing/reading. 40 41 42 from mod_pywebsocket import util 43 44 45 # Exceptions 46 47 48 class ConnectionTerminatedException(Exception): 49 """This exception will be raised when a connection is terminated 50 unexpectedly. 51 """ 52 53 pass 54 55 56 class InvalidFrameException(ConnectionTerminatedException): 57 """This exception will be raised when we received an invalid frame we 58 cannot parse. 59 """ 60 61 pass 62 63 64 class BadOperationException(Exception): 65 """This exception will be raised when send_message() is called on 66 server-terminated connection or receive_message() is called on 67 client-terminated connection. 68 """ 69 70 pass 71 72 73 class UnsupportedFrameException(Exception): 74 """This exception will be raised when we receive a frame with flag, opcode 75 we cannot handle. Handlers can just catch and ignore this exception and 76 call receive_message() again to continue processing the next frame. 77 """ 78 79 pass 80 81 82 class InvalidUTF8Exception(Exception): 83 """This exception will be raised when we receive a text frame which 84 contains invalid UTF-8 strings. 85 """ 86 87 pass 88 89 90 class StreamBase(object): 91 """Base stream class.""" 92 93 def __init__(self, request): 94 """Construct an instance. 95 96 Args: 97 request: mod_python request. 98 """ 99 100 self._logger = util.get_class_logger(self) 101 102 self._request = request 103 104 def _read(self, length): 105 """Reads length bytes from connection. In case we catch any exception, 106 prepends remote address to the exception message and raise again. 107 108 Raises: 109 ConnectionTerminatedException: when read returns empty string. 110 """ 111 112 bytes = self._request.connection.read(length) 113 if not bytes: 114 raise ConnectionTerminatedException( 115 'Receiving %d byte failed. Peer (%r) closed connection' % 116 (length, (self._request.connection.remote_addr,))) 117 return bytes 118 119 def _write(self, bytes): 120 """Writes given bytes to connection. In case we catch any exception, 121 prepends remote address to the exception message and raise again. 122 """ 123 124 try: 125 self._request.connection.write(bytes) 126 except Exception, e: 127 util.prepend_message_to_exception( 128 'Failed to send message to %r: ' % 129 (self._request.connection.remote_addr,), 130 e) 131 raise 132 133 def receive_bytes(self, length): 134 """Receives multiple bytes. Retries read when we couldn't receive the 135 specified amount. 136 137 Raises: 138 ConnectionTerminatedException: when read returns empty string. 139 """ 140 141 bytes = [] 142 while length > 0: 143 new_bytes = self._read(length) 144 bytes.append(new_bytes) 145 length -= len(new_bytes) 146 return ''.join(bytes) 147 148 def _read_until(self, delim_char): 149 """Reads bytes until we encounter delim_char. The result will not 150 contain delim_char. 151 152 Raises: 153 ConnectionTerminatedException: when read returns empty string. 154 """ 155 156 bytes = [] 157 while True: 158 ch = self._read(1) 159 if ch == delim_char: 160 break 161 bytes.append(ch) 162 return ''.join(bytes) 163 164 165 # vi:sts=4 sw=4 et 166