def __init__(self,_WSServer): self._WSServer = _WSServer self.conn = '' self.addr = '' self.setStatus('CLOSED') self._WSController = WSController(self)
class WSClient: # WSClient connection status CONNECTION_STATUS = { 'CONNECTING': 0x0, 'OPEN': 0x1, 'CLOSING': 0x2, 'CLOSED': 0x3 } ## Constructor # @param _WSServer WebSocket Server object attached to client. def __init__(self,_WSServer): self._WSServer = _WSServer self.conn = '' self.addr = '' self.setStatus('CLOSED') self._WSController = WSController(self) ## Set current connection status # @param status Status of the socket. Can be 'CONNECTING', 'OPEN', 'CLOSING' or 'CLOSED'. def setStatus(self, status=''): if (status in self.CONNECTION_STATUS): self.status = self.CONNECTION_STATUS[status] ## Test current connection status # @param status Status of the socket. Can be 'CONNECTING', 'OPEN', 'CLOSING' or 'CLOSED'. def hasStatus(self, status): if (status in self.CONNECTION_STATUS): return self.status == self.CONNECTION_STATUS[status] return False ## Real socket bytes reception # @param bufsize Buffer size to return. def receive(self, bufsize): bytes = self.conn.recv(bufsize) if not bytes: print 'Client left', repr(self.conn) self._WSServer.remove(self) self.close() return '' return bytes ## Try to read an amount bytes # @param bufsize Buffer size to fill. def read(self, bufsize): remaining = bufsize bytes = '' while remaining and self.hasStatus('OPEN'): bytes += self.receive(remaining) remaining = bufsize - len(bytes) return bytes ## Read data until line return (used by handshake) def readlineheader(self): line = [] while self.hasStatus('CONNECTING') and len(line)<1024: c = self.receive(1) line.append(c) #print 'readlineheader: ', line if c == "\n": break return "".join(line) ## Send handshake according to RFC def hanshake(self): headers = {} # Ignore first line with GET line = self.readlineheader() while self.hasStatus('CONNECTING'): if len(headers)>64: raise ValueError('Header too long.') line = self.readlineheader() if not self.hasStatus('CONNECTING'): raise ValueError('Client left.') if len(line) == 0 or len(line) == 1024: raise ValueError('Invalid line in header.') if line == '\r\n': break # take care with strip ! # >>> import string;string.whitespace # '\t\n\x0b\x0c\r ' line = line.strip() # take care with split ! # >>> a='key1:value1:key2:value2';a.split(':',1) # ['key1', 'value1:key2:value2'] kv = line.split(':', 1) if len(kv) == 2: key, value = kv k = key.strip().lower() v = value.strip() headers[k] = v else: raise ValueError('Invalid header key/value.') #print headers if not len(headers): raise ValueError('Reading headers failed.') if not 'sec-websocket-version' in headers: raise ValueError('Missing parameter "Sec-WebSocket-Version".') if not 'sec-websocket-key' in headers: raise ValueError('Missing parameter "Sec-WebSocket-Key".') if not 'host' in headers: raise ValueError('Missing parameter "Host".') if not 'origin' in headers: raise ValueError('Missing parameter "Origin".') if (int(headers['sec-websocket-version']) != WSSettings.VERSION): raise ValueError('Wrong protocol version %s.' % WSSettings.VERSION) accept = base64.b64encode(hashlib.sha1(headers['sec-websocket-key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest()) bytes = ('HTTP/1.1 101 Switching Protocols\r\n' 'Upgrade: websocket\r\n' 'Connection: Upgrade\r\n' 'Sec-WebSocket-Origin: %s\r\n' 'Sec-WebSocket-Location: ws://%s\r\n' 'Sec-WebSocket-Accept: %s\r\n' 'Sec-WebSocket-Version: %s\r\n' '\r\n') % (headers['origin'], headers['host'], accept, headers['sec-websocket-version']) print '--- HANDSHAKE ---' print bytes print '-----------------' self.send(bytes) ## Handle incoming datas # @param conn Socket of WebSocket client (from WSServer). # @param addr Adress of WebSocket client (from WSServer). def handle(self, conn, addr): self.conn = conn self.addr = addr self.setStatus('CONNECTING') try: self.hanshake() except ValueError as error: self._WSServer.remove(self) self.close() raise ValueError('Client rejected: ' + str(error)) else: _WSDecoder = WSDecoder() self.setStatus('OPEN') while self.hasStatus('OPEN'): try: ctrl, data = _WSDecoder.decode(self) except ValueError as (closing_code, message): if self.hasStatus('OPEN'): # context can change... self._WSController.kill(closing_code,'WSDecoder::'+str(message)) break else: print '--- INCOMING DATAS ---' self._WSController.run(ctrl, data)