def __init__(self, server, loop, sock, address_info): self.server = server self.loop = loop self.sock = sock self.sock.setblocking(0) self.address_info = address_info self.pipe_buffer = PipeBuffer() self.watchers = { 'recv': loop.io(sock.fileno(), EV_READ, self.recv_data), 'send': loop.io(sock.fileno(), EV_WRITE, self.send_data, 0), 'timeout': loop.timer(self.IO_TIMEOUT, self.IO_TIMEOUT, self.timeout), } self.http_parser = HTTPParser()
def authorize(self, client_address: tuple[str, int], client_request: HTTPParser) -> Optional[HTTPResponse]: path = client_request.request_path secret = self.protected_paths[path]["secret"] timeout = self.protected_paths[path]["timeout"] prefix: str = self.protected_paths[path]["prefix"] if secret: # This path is token-protected if not path.startswith(prefix): # Incorrect prefix # FIXME: note that something is probably wrong with # the configuration here, we should probably log / # warn the admin return AUTH_FAILURE else: # Get rid of prefix and slashes path = path[len(prefix):].strip("/") if path.count("/") < 2: # Not enough components to be a tokenised path return AUTH_FAILURE # Split into token, timestamp, and path token, timestamp, path = path.split("/", 2) # Check the token is valid if token != hashlib.md5(secret + "/" + path + timestamp).hexdigest(): # Invalid token return AUTH_FAILURE # Check the timeout is not expired, if needed if timeout and (int(time.time()) - timeout) > int( timestamp, 16): return AUTH_FAILURE # We have to remove the token and timestamp from the original # path or else the server won't find the correct handler # afterwards client_request.request_path = "/".join([prefix, path]) return AUTH_SUCCESS return None
class InboundClient(object): IO_TIMEOUT = 10 MAX_REQUEST_SIZE = 4096 def __init__(self, server, loop, sock, address_info): self.server = server self.loop = loop self.sock = sock self.sock.setblocking(0) self.address_info = address_info self.pipe_buffer = PipeBuffer() self.watchers = { 'recv': loop.io(sock.fileno(), EV_READ, self.recv_data), 'send': loop.io(sock.fileno(), EV_WRITE, self.send_data, 0), 'timeout': loop.timer(self.IO_TIMEOUT, self.IO_TIMEOUT, self.timeout), } self.http_parser = HTTPParser() def start(self): self.watchers['recv'].start() self.watchers['timeout'].start() # send is not started yet def stop(self): for watcher in self.watchers.values(): watcher.stop() self.sock.close() self.pipe_buffer.close() self.server.remove_client(self) self.server.logger.debug('Stopped client: %s', self) def recv_data(self, watcher, revents): try: data = self.sock.recv(self.MAX_REQUEST_SIZE, socket.MSG_PEEK) self.http_parser.execute(data) if self.http_parser.has_error(): self.stop() elif self.http_parser.is_finished(): # IO_TIMEOUT seconds at most for the response to come in self.watchers['timeout'].reset() data = self.sock.recv(self.MAX_REQUEST_SIZE) # We're not interested in any more data from this peer self.sock.shutdown(socket.SHUT_RD) watcher.stop() # Ask our server for the response, cached or not self.server.proxy(self, self.http_parser, data) except: self.server.logger.exception('While recv_data():') raise def start_sending(self): self.watchers['send'].start() def send_data(self, watcher, revents): self.watchers['timeout'].reset() self.server.logger.debug('Sending (%d - %d) = %d bytes to %s', self.pipe_buffer.size, watcher.data, self.pipe_buffer.size - watcher.data, self) watcher.data += splice(self.pipe_buffer.reader, None, self.sock.fileno(), None, self.pipe_buffer.size - watcher.data)[0] if watcher.data >= self.pipe_buffer.size: self.server.logger.debug('Done sending data to %s', self) self.stop() self.server.logger.debug('Sent %d bytes to %s', watcher.data, self) def timeout(self, watcher, revents): self.stop()