def _close(self): #called once the socket should be finally really closed if self.inQueueSize > 0: #not empty receive buffer self.i2pSockStatus.setRecvable(False, self.connId) if self.errorReason is None and self.connected and (not self.waitingForClose) and self.outQueueSize < self.outMaxQueueSize: #socket did not fail up to now, was not waiting for close and has free space in its send buffer => it was sendable self.i2pSockStatus.setSendable(False, self.connId) if self.errorReason is not None: #socket failed before self.i2pSockStatus.setErrored(False, self.connId) elif self.state == 'init': #socket was waiting for name query self.failFunc(self.connId) elif self.state == 'connected' or self.state == 'connecting': #socket was ok assert self.samId is not None, 'uhm, we need the id ...' self.failFunc(self.connId) self.sendFunc(SamMessages.streamCloseMessage(self.samId)) self.state = 'closed' self.removeFunc(self.connId)
def _handleMessages(self, messages): for message in messages: messageType = message['msgType'] messageParas = message['msgParas'] if messageType=='HELLO REPLY': #handshake reply if messageParas['RESULT'].upper()=='OK' and messageParas['VERSION']=='2.0': #send session create message assert len(self.outQueue)==0, "just handshaked and stuff to send?!" self._sendOverRealSocket(SamMessages.sessionCreateMessage(self.sessionType, self.sessionName, self.sessionDirection, self.sessionOptions)) else: #something borked self._failDestination('Invalid HELLO REPLY: Result<%s> Version<%s>' % (messageParas['RESULT'], messageParas['VERSION'])) elif messageType=='SESSION STATUS': #session established if not messageParas['RESULT'].upper()=='OK': #urgh self._failDestination('Failed to setup session: "%s"' % (messageParas['RESULT'],)) else: #ok, session is established self.sessionEstablished = True self._sendOverRealSocket('NAMING LOOKUP NAME=ME\n') self._establishedDestination() elif messageType=='NAMING REPLY': #reply to destination request if not messageParas['NAME'].upper() == 'ME': self._handleNameLookup(message) elif messageParas['RESULT'].upper() == 'OK': self.sessionKey = messageParas['VALUE'] else: self._handleCustomMessage(message)
def sendEvent(self): #called to send data out of the outbound queue - triggered when the sam bridge allows further sending assert self.state == 'connected', 'wrong state, expected "connected", got "%s"' % (self.state,) self.sendable = True if self.outQueueSize == 0: #nothing to send data = None if self.waitingForClose: #was waiting for close and done sending, so close it now self._close() elif self.outQueueSize <= 32768: #fits into a single message data = ''.join(self.outQueue) dataSize = len(data) self.outQueue.clear() self.outQueueSize = 0 else: #too much data for a single message data = deque() dataSize = 0 while dataSize < 32768: #add chunks until the limit is reached dataChunk = self.outQueue.popleft() dataChunkLen = len(dataChunk) if dataSize + dataChunkLen < 32768: #take entire chunk data.append(dataChunk) dataSize += dataChunkLen self.outQueueSize -= dataChunkLen else: #only take a part of the chunk useableDataChunkLen = 32768 - dataSize data.append(dataChunk[:useableDataChunkLen]) self.outQueue.appendleft(dataChunk[useableDataChunkLen:]) self.outQueueSize -= useableDataChunkLen dataSize += useableDataChunkLen data = ''.join(data) if data is not None: #something to send if (not self.waitingForClose) and \ self.outQueueSize < self.outMaxQueueSize and \ (self.outQueueSize + dataSize) >= self.outMaxQueueSize: #buffer was full and is not full anymore self.i2pSockStatus.setSendable(True, self.connId) #send data to the sam bridge self.sendFunc(SamMessages.streamSendMessage(self.samId, data)) #remember data, in case sam rejects it self.outMessage = data #don't send anything until the sam bridge send us its status self.sendable = False
def connectEvent(self): #called when the socket connects assert (self.state == 'connecting' and self.direction == 'out') or (self.state == 'init' and self.direction == 'in'), 'wrong state "%s", direction "%s"' % (self.state, self.direction) self.state = 'connected' self.connected = True self.sendable = True self.inRecvLimit = self.inMaxQueueSize self.i2pSockStatus.setSendable(True, self.connId) self.sendFunc(SamMessages.streamReceiveLimitMessage(self.samId, self.inRecvLimit))
def _addNameLookupQuery(self, connId, name, func, funcArgs=[], funcKw={}): assert not connId in self.connIdToName, 'already running a lookup?!' self.connIdToName[connId] = name if name in self.nameLookups: #already running a lookup self.nameLookups[name][connId] = (func, funcArgs, funcKw) else: #start a new one self.nameLookups[name] = {connId:(func, funcArgs, funcKw)} self._sendOverRealSocket(SamMessages.nameLookup(name))
def recv(self, maxBytes, peekOnly): #called to recv data out of inbound queue assert self.state == 'connected' or self.state == 'failed', 'wrong state, expected "connected" or "failed", got "%s"' % (self.state,) availableBytes = self.inQueueSize if maxBytes == -1 or availableBytes <= maxBytes: #take everything available data = ''.join(self.inQueue) if not peekOnly: self.inQueue.clear() self.inQueueSize = 0 self.i2pSockStatus.setRecvable(False, self.connId) else: #too much, only take a part data = deque() dataSize = 0 count = 0 while dataSize < maxBytes: #add chunks until the limit is reached if peekOnly: dataChunk = self.inQueue[count] count += 1 else: dataChunk = self.inQueue.popleft() dataChunkLen = len(dataChunk) if dataSize + dataChunkLen < maxBytes: #take entire chunk data.append(dataChunk) dataSize += dataChunkLen if not peekOnly: self.inQueueSize -= dataChunkLen else: #only take a part of the chunk useableDataChunkLen = maxBytes - dataSize data.append(dataChunk[:useableDataChunkLen]) dataSize += useableDataChunkLen if not peekOnly: self.inQueue.appendleft(dataChunk[useableDataChunkLen:]) self.inQueueSize -= useableDataChunkLen data = ''.join(data) allowedBytes = self.inMaxQueueSize - self.inQueueSize if allowedBytes > 0: #free buffer space above threshold recvLimit = allowedBytes + self.inBytesReceived if self.samId is not None and recvLimit > (self.inRecvLimitThreshold + self.inRecvLimit): #new recv limit, notify sam bridge, if connected self.inRecvLimit = recvLimit self.sendFunc(SamMessages.streamReceiveLimitMessage(self.samId, recvLimit)) return data
def connect(self, remoteDest=None, samId=None): #called if the socket should send a connect message if remoteDest is not None: self.remoteDest = remoteDest if samId is not None: self.samId = samId assert self.remoteDest is not None, 'uhm, we need a destination ...' assert self.samId is not None, 'uhm, we need a id ...' assert self.state == 'init', 'wrong state, expected "init", got "%s"' % (self.state,) self.state = 'connecting' self.sendFunc(SamMessages.streamConnectMessage(self.samId, self.remoteDest))
def send(self, target, data): self._sendOverRealSocket(SamMessages.datagramSendMessage(target, data)) return len(data)
def recvEvent(self): data = self.sock.recv() dataLen = len(data) messages = [] offset = 0 #process data while not offset >= dataLen: #loop until all received data was processed if self.inMessage is None: ##there is no finished message that needs bulk data endOfMessage = data.find('\n', offset) if endOfMessage == -1: #only the beginning of a message, store it in the inbound queue self.inQueue.append(data[offset:]) offset = dataLen else: #the whole message or the ending message = data[offset:endOfMessage] offset = endOfMessage + 1 if len(self.inQueue) > 0: #get the beginning of the message out of the queue self.inQueue.append(message) message = ''.join(self.inQueue) self.inQueue.clear() #parse message if self.log is not None: self.log.debug("Got Message: \"%s\"", message) message = SamMessages.parseMessage(message) #check if we need to wait for more data if 'SIZE' in message['msgParas']: message['Data'] = [] message['DataCurrentLen'] = 0 message['DataTargetLen'] = int(message['msgParas']['SIZE']) self.inMessage = message else: messages.append(message) else: ##only missing a few bytes here ... assert len(self.inQueue) == 0, 'receiving data but stuff in the inqueue?!' missingBytes = self.inMessage['DataTargetLen'] - self.inMessage['DataCurrentLen'] remainingBytes = dataLen - offset if remainingBytes >= missingBytes: #got all if self.log is not None: self.log.debug("Got missing %i bytes for message", missingBytes) self.inMessage['Data'].append(data[offset:offset+missingBytes]) self.inMessage['DataCurrentLen'] += missingBytes assert self.inMessage['DataCurrentLen'] == self.inMessage['DataTargetLen'], 'message finished but too short?!' offset += missingBytes messages.append(self.inMessage) self.inMessage = None else: #still missing a bit if self.log is not None: self.log.debug("Got %i bytes for message but still missing %i bytes", remainingBytes, (missingBytes - remainingBytes)) self.inMessage['Data'].append(data[offset:]) self.inMessage['DataCurrentLen'] += remainingBytes offset += remainingBytes #handle messages self._handleMessages(messages)