def _send_frame(self, message, opcode): """Send a frame over the websocket with message as its payload.""" if self._closing: ws_logger.warning('websocket connection is closing.') msg_length = len(message) use_mask = self.use_mask if use_mask: mask_bit = 0x80 else: mask_bit = 0 if msg_length < 126: header = PACK_LEN1(0x80 | opcode, msg_length | mask_bit) elif msg_length < (1 << 16): header = PACK_LEN2(0x80 | opcode, 126 | mask_bit, msg_length) else: header = PACK_LEN3(0x80 | opcode, 127 | mask_bit, msg_length) if use_mask: mask = self.randrange(0, 0xffffffff) mask = mask.to_bytes(4, 'big') message = _websocket_mask(mask, bytearray(message)) self.writer.write(header + mask + message) self._output_size += len(header) + len(mask) + len(message) else: if len(message) > MSG_SIZE: self.writer.write(header) self.writer.write(message) else: self.writer.write(header + message) self._output_size += len(header) + len(message) if self._output_size > self._limit: self._output_size = 0 return self.writer.drain() return noop()
def do_handshake(method, headers, transport, protocols=()): """Prepare WebSocket handshake. It return HTTP response code, response headers, websocket parser, websocket writer. It does not perform any IO. `protocols` is a sequence of known protocols. On successful handshake, the returned response headers contain the first protocol in this list which the server also knows. """ # WebSocket accepts only GET if method.upper() != hdrs.METH_GET: raise errors.HttpProcessingError( code=405, headers=((hdrs.ALLOW, hdrs.METH_GET),)) if 'websocket' != headers.get(hdrs.UPGRADE, '').lower().strip(): raise errors.HttpBadRequest( message='No WebSocket UPGRADE hdr: {}\n Can ' '"Upgrade" only to "WebSocket".'.format(headers.get(hdrs.UPGRADE))) if 'upgrade' not in headers.get(hdrs.CONNECTION, '').lower(): raise errors.HttpBadRequest( message='No CONNECTION upgrade hdr: {}'.format( headers.get(hdrs.CONNECTION))) # find common sub-protocol between client and server protocol = None if hdrs.SEC_WEBSOCKET_PROTOCOL in headers: req_protocols = [str(proto.strip()) for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(',')] for proto in req_protocols: if proto in protocols: protocol = proto break else: # No overlap found: Return no protocol as per spec ws_logger.warning( 'Client protocols %r don’t overlap server-known ones %r', req_protocols, protocols) # check supported version version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, '') if version not in ('13', '8', '7'): raise errors.HttpBadRequest( message='Unsupported version: {}'.format(version), headers=((hdrs.SEC_WEBSOCKET_VERSION, '13'),)) # check client handshake for validity key = headers.get(hdrs.SEC_WEBSOCKET_KEY) try: if not key or len(base64.b64decode(key)) != 16: raise errors.HttpBadRequest( message='Handshake error: {!r}'.format(key)) except binascii.Error: raise errors.HttpBadRequest( message='Handshake error: {!r}'.format(key)) from None response_headers = [ (hdrs.UPGRADE, 'websocket'), (hdrs.CONNECTION, 'upgrade'), (hdrs.TRANSFER_ENCODING, 'chunked'), (hdrs.SEC_WEBSOCKET_ACCEPT, base64.b64encode( hashlib.sha1(key.encode() + WS_KEY).digest()).decode())] if protocol: response_headers.append((hdrs.SEC_WEBSOCKET_PROTOCOL, protocol)) # response code, headers, parser, writer, protocol return (101, response_headers, WebSocketParser, WebSocketWriter(transport), protocol)
def do_handshake(method, headers, transport, protocols=(), write_buffer_size=DEFAULT_LIMIT): """Prepare WebSocket handshake. It return HTTP response code, response headers, websocket parser, websocket writer. It does not perform any IO. `protocols` is a sequence of known protocols. On successful handshake, the returned response headers contain the first protocol in this list which the server also knows. `write_buffer_size` max size of write buffer before `drain()` get called. """ # WebSocket accepts only GET if method.upper() != hdrs.METH_GET: raise errors.HttpProcessingError(code=405, headers=((hdrs.ALLOW, hdrs.METH_GET), )) if 'websocket' != headers.get(hdrs.UPGRADE, '').lower().strip(): raise errors.HttpBadRequest( message='No WebSocket UPGRADE hdr: {}\n Can ' '"Upgrade" only to "WebSocket".'.format(headers.get(hdrs.UPGRADE))) if 'upgrade' not in headers.get(hdrs.CONNECTION, '').lower(): raise errors.HttpBadRequest( message='No CONNECTION upgrade hdr: {}'.format( headers.get(hdrs.CONNECTION))) # find common sub-protocol between client and server protocol = None if hdrs.SEC_WEBSOCKET_PROTOCOL in headers: req_protocols = [ str(proto.strip()) for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(',') ] for proto in req_protocols: if proto in protocols: protocol = proto break else: # No overlap found: Return no protocol as per spec ws_logger.warning( 'Client protocols %r don’t overlap server-known ones %r', req_protocols, protocols) # check supported version version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, '') if version not in ('13', '8', '7'): raise errors.HttpBadRequest( message='Unsupported version: {}'.format(version), headers=((hdrs.SEC_WEBSOCKET_VERSION, '13'), )) # check client handshake for validity key = headers.get(hdrs.SEC_WEBSOCKET_KEY) try: if not key or len(base64.b64decode(key)) != 16: raise errors.HttpBadRequest( message='Handshake error: {!r}'.format(key)) except binascii.Error: raise errors.HttpBadRequest( message='Handshake error: {!r}'.format(key)) from None response_headers = [ (hdrs.UPGRADE, 'websocket'), (hdrs.CONNECTION, 'upgrade'), (hdrs.TRANSFER_ENCODING, 'chunked'), (hdrs.SEC_WEBSOCKET_ACCEPT, base64.b64encode(hashlib.sha1(key.encode() + WS_KEY).digest()).decode()) ] if protocol: response_headers.append((hdrs.SEC_WEBSOCKET_PROTOCOL, protocol)) # response code, headers, parser, writer, protocol return (101, response_headers, WebSocketParser, WebSocketWriter(transport, limit=write_buffer_size), protocol)
def do_handshake(method, headers, transport, protocols=()): """Prepare WebSocket handshake. It return http response code, response headers, websocket parser, websocket writer. It does not perform any IO. `protocols` is a sequence of known protocols. On successful handshake, the returned response headers contain the first protocol in this list which the server also knows.""" # WebSocket accepts only GET if method.upper() != 'GET': raise errors.HttpProcessingError(code=405, headers=(('Allow', 'GET'), )) if 'websocket' != headers.get('UPGRADE', '').lower().strip(): raise errors.HttpBadRequest(message='No WebSocket UPGRADE hdr: {}\n' 'Can "Upgrade" only to "WebSocket".'. format(headers.get('UPGRADE'))) if 'upgrade' not in headers.get('CONNECTION', '').lower(): raise errors.HttpBadRequest( message='No CONNECTION upgrade hdr: {}'.format( headers.get('CONNECTION'))) # find common sub-protocol between client and server protocol = None if 'SEC-WEBSOCKET-PROTOCOL' in headers: req_protocols = [ str(proto.strip()) for proto in headers['SEC-WEBSOCKET-PROTOCOL'].split(',') ] for proto in req_protocols: if proto in protocols: protocol = proto break else: # No overlap found: Return no protocol as per spec ws_logger.warning( 'Client protocols %r don’t overlap server-known ones %r', protocols, req_protocols) # check supported version version = headers.get('SEC-WEBSOCKET-VERSION') if version not in ('13', '8', '7'): raise errors.HttpBadRequest( message='Unsupported version: {}'.format(version), headers=(('Sec-WebSocket-Version', '13', '8', '7'), )) # check client handshake for validity key = headers.get('SEC-WEBSOCKET-KEY') try: if not key or len(base64.b64decode(key)) != 16: raise errors.HttpBadRequest( message='Handshake error: {!r}'.format(key)) except binascii.Error: raise errors.HttpBadRequest( message='Handshake error: {!r}'.format(key)) from None response_headers = [ ('UPGRADE', 'websocket'), ('CONNECTION', 'upgrade'), ('TRANSFER-ENCODING', 'chunked'), ('SEC-WEBSOCKET-ACCEPT', base64.b64encode(hashlib.sha1(key.encode() + WS_KEY).digest()).decode()) ] if protocol: response_headers.append(('SEC-WEBSOCKET-PROTOCOL', protocol)) # response code, headers, parser, writer, protocol return (101, response_headers, WebSocketParser, WebSocketWriter(transport), protocol)