def socket(): ws = request.environ.get('wsgi.websocket') if not ws: print "! WebSocket failed to initialize" return "WebSocket failed to initialize" token, email = None, None try: # Await client confirmation data = ws.receive() try: message = json.loads(data) token = message['token'] email = message['email'] except: raise WebSocketError("WebSocket data missing or incorrect type") if not db.get_email_by_token(token): ws.send( json.dumps({ 'success': False, 'message': "You are not signed in." })) raise WebSocketError("User is not signed in") # Save connection sockets.add(token, email, ws) print "+ Connected with:", email, '({})'.format(request.remote_addr) sockets.success(token, "Connection established.") sockets.update_online_status() # Await client closing connection while True: data = ws.receive() if data == None: sockets.remove(token) break except WebSocketError as error: print "! WebSocketError:", error finally: print "- Disconnected from:", email ws.close() sockets.update_online_status() return ''
def send(self, message, opcode=OPCODE_TEXT): """Send a frame over the websocket with message as its payload Keyword args: opcode -- the opcode to use (default OPCODE_TEXT) """ if self.fobj is None: return if not self._is_valid_opcode(opcode): raise ValueError('Invalid opcode %d' % opcode) if opcode == self.OPCODE_TEXT: message = self._encode_text(message) # TODO: implement fragmented messages mask_bit = 0 fin = 1 ## +-+-+-+-+-------+ ## |F|R|R|R| opcode| ## |I|S|S|S| (4) | ## |N|V|V|V| | ## | |1|2|3| | ## +-+-+-+-+-------+ header = chr((fin << 7) | (0 << 6) | # RSV1 (0 << 5) | # RSV2 (0 << 4) | # RSV3 opcode) ## +-+-------------+-------------------------------+ ## |M| Payload len | Extended payload length | ## |A| (7) | (16/63) | ## |S| | (if payload len==126/127) | ## |K| | | ## +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + ## | Extended payload length continued, if payload len == 127 | ## + - - - - - - - - - - - - - - - +-------------------------------+ msg_length = len(message) if opcode == self.OPCODE_TEXT: message = struct.pack('!%ds' % msg_length, message) if msg_length < 126: header += chr(mask_bit | msg_length) elif msg_length < (1 << 16): header += chr(mask_bit | 126) + struct.pack('!H', msg_length) elif msg_length < (1 << 63): header += chr(mask_bit | 127) + struct.pack('!Q', msg_length) else: raise FrameTooLargeException() with self._writelock: try: self._write(header + message) except socket_error: self._close() raise WebSocketError()
def proxy_user_websocket(ipaddr, port, path): def c2s(client, server): while True: inp = client.receive() if inp is None: raise WebSocketError() server.send(inp) def get_headers(): headers = [] #for header in request.environ: # if not header.startswith('HTTP_'): # continue # if not header.startswith('HTTP_SEC_') \ # and not header.startswith('HTTP_ACCEPT_') \ # and not header.startswith('HTTP_USER_AGENT'): # continue # upper = True # k = '' # for c in header[5:].replace('_', '-').lower(): # if upper: # k += c.upper() # upper = False # else: # k += c # if c == '-': # upper = True # headers.append('%s: %s' % (k, request.environ[header])) return headers #https://stackoverflow.com/questions/18240358/html5-websocket-connecting-to-python client = request.environ['wsgi.websocket'] url = '%s:%d' % (ipaddr, port) if len(path) > 0: url += '/' + path logging.info('websocket: ' + url) headers = [] #headers = get_headers() #logging.info('headers: ' + str(headers)) server = websocket.create_connection("ws://" + url, header=headers) try: spawn(c2s, client, server) while True: inp = server.recv() if inp is None: raise WebSocketError() client.send(inp) except WebSocketError as e: logging.error(e) except client.WebSocketConnectionClosedException: pass return json.dumps({'status': 200})
def receive(self): read = self.fobj.read while self.fobj is not None: frame_str = read(1) if not frame_str: self.close() return else: frame_type = ord(frame_str) if frame_type == 0x00: bytes = self._read_until() return bytes.decode("utf-8", "replace") else: raise WebSocketError("Received an invalid " "frame_type=%r" % frame_type)
def _read_until(self): bytes = [] read = self.fobj.read while True: if self.fobj is None: msg = ''.join(bytes) raise WebSocketError('Connection closed unexpectedly ' 'while reading message: %r' % msg) byte = read(1) if ord(byte) != 0xff: bytes.append(byte) else: break return ''.join(bytes)
def _message_length(self): length = 0 while True: if self.fobj is None: raise WebSocketError('Connenction closed unexpectedly while' ' reading message length') byte_str = self.fobj.read(1) if not byte_str: return 0 else: byte = ord(byte_str) if byte != 0x00: length = length * 128 + (byte & 0x7f) if (byte & 0x80) != 0x80: break return length
def receive(self): """Return the next frame from the socket.""" if self.fobj is None: raise WebSocketError('WS closed') while True: data0 = self._read(2) if data0 is None: self._close() raise WebSocketError('_read nothing') if not data0: # штатное закрытие по инициативе другой стороны break fin, opcode, mask, length = self._parse_header(data0) if length < 126: data1 = '' elif length == 126: data1 = self._read(2) if len(data1) != 2: self._close() raise WebSocketError('Incomplete read while reading ' '2-byte length: %r' % (data0 + data1)) length = struct.unpack('!H', data1)[0] elif length == 127: data1 = self._read(8) if len(data1) != 8: self._close() raise WebSocketError('Incomplete read while reading ' '8-byte length: %r' % (data0 + data1)) length = struct.unpack('!Q', data1)[0] else: self._close() raise WebSocketError('Invalid length: %r' % data0) # Unmask the payload if necessary if mask and length: data2 = self._read(4) if len(data2) != 4: self._close() raise WebSocketError('Incomplete read while reading ' 'mask: %r' % (data0 + data1 + data2)) masking_key = struct.unpack('!BBBB', data2) else: data2 = '' if length: payload = self._read(length) if len(payload) != length: self._close() args = (length, data0 + data1 + data2, payload) raise WebSocketError('Incomplete read (expected message ' 'of %s bytes): %r %r' % args) else: payload = '' if mask: # XXX message from client actually should always be masked masked_payload = bytearray(payload) for i in range(len(masked_payload)): masked_payload[i] = masked_payload[i] ^ masking_key[i % 4] payload = masked_payload if opcode == self.OPCODE_TEXT: self._first_opcode = opcode if payload: # XXX given that we have OPCODE_CONTINUATION, # shouldn't we just reset _chunks here? self._chunks.extend(payload) elif opcode == self.OPCODE_BINARY: self._first_opcode = opcode if payload: self._chunks.extend(payload) elif opcode == self.OPCODE_CONTINUATION: self._chunks.extend(payload) elif opcode == self.OPCODE_CLOSE: if length >= 2: reason, message = struct.unpack('!H%ds' % (length - 2), buffer(payload)) else: reason = message = None self.close(self.REASON_NORMAL, '') return Closed(reason, message) elif opcode == self.OPCODE_PING: self.send(payload, opcode=self.OPCODE_PONG) continue elif opcode == self.OPCODE_PONG: continue else: self._close() raise WebSocketError("Unexpected opcode=%r" % (opcode, )) if fin == 1: if self._first_opcode == self.OPCODE_TEXT: msg = self._chunks.decode("utf-8") else: msg = self._chunks self._first_opcode = False self._chunks = bytearray() return msg
def _read(self, size): try: return self.fobj.read(size) except: raise WebSocketError("read failed")
def _parse_header(self, data): if len(data) != 2: self.close() raise WebSocketError('Incomplete read while reading header: %r' % data) first_byte, second_byte = struct.unpack('!BB', data) fin = (first_byte >> 7) & 1 rsv1 = (first_byte >> 6) & 1 rsv2 = (first_byte >> 5) & 1 rsv3 = (first_byte >> 4) & 1 opcode = first_byte & 0xf # frame-fin = %x0 ; more frames of this message follow # / %x1 ; final frame of this message # frame-rsv1 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise # frame-rsv2 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise # frame-rsv3 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise if rsv1 or rsv2 or rsv3: self.close() raise WebSocketError('Reserved bits cannot be set: %r' % data) #if self._is_invalid_opcode(opcode): # raise WebSocketError('Invalid opcode %x' % opcode) # control frames cannot be fragmented if opcode > 0x7 and fin == 0: self.close() raise WebSocketError('Control frames cannot be fragmented: %r' % data) if len(self._chunks) > 0 and fin == 0 \ and opcode != self.OPCODE_CONTINUATION: self.close(self.REASON_PROTOCOL_ERROR, 'Received new fragment frame with non-zero opcode') raise WebSocketError('Received new fragment frame with ' 'non-zero opcode: %r' % data) if len(self._chunks) > 0 and fin == 1 \ and (self.OPCODE_TEXT <= opcode <= self.OPCODE_BINARY): self.close( self.REASON_PROTOCOL_ERROR, 'Received new unfragmented data frame during ' 'fragmented message') raise WebSocketError('Received new unfragmented data frame ' 'during fragmented message: %r' % data) mask = (second_byte >> 7) & 1 length = (second_byte) & 0x7f #if not self.MASK & length_octet: # TODO: where is this in the docs? # self.close(self.REASON_PROTOCOL_ERROR, 'MASK must be set') # Control frames MUST have a payload length of 125 bytes or less if opcode > 0x7 and length > 125: self.close() raise FrameTooLargeException("Control frame payload cannot " "be larger than 125 bytes: %r" % data) return fin, opcode, mask, length
def c2s(client, server): while True: inp = client.receive() if inp is None: raise WebSocketError() server.send(inp)