예제 #1
0
def test_header_happy_case():
    parser = HTTPHeaderParser()
    rst = parser.feed(Ref(HTTP_10_GET()))
    assert isinstance(rst, HTTPRequestHeader)
    assert rst.method == b'GET'
    assert rst.location.scheme == 'http'
    assert rst.location.netloc == 'example.com'
    assert rst.location.path == '/path'
    assert rst.location.query == 'key=value'
    assert rst.version == b'HTTP/1.0'
    assert rst.headers == {b'Header': (0, b'header-val')}
예제 #2
0
 def __init__(self, client_socket, client_address, bfc: BFCNode,
              ev_server: EventServer):
     self._client_socket = client_socket
     self._client_address = client_address
     self._client_recv_buf = b""
     """Data RECEIVED from the client."""
     self._client_send_buf = b""
     """Data to SEND to the client."""
     self._client_writable: bool = False
     """If the client is ready to accept data."""
     self._state = _WorkerState.CREATED
     self._ev_server = ev_server
     self._http_header_parser = HTTPHeaderParser()
     self._http_body_parser: HTTPBodyParser = None
     self._cur_session: _WorkerSession = None
     self._bfc = bfc
예제 #3
0
class _Worker(EventConsumer):
    """Handles a worker connection."""
    def __init__(self, client_socket, client_address, bfc: BFCNode,
                 ev_server: EventServer):
        self._client_socket = client_socket
        self._client_address = client_address
        self._client_recv_buf = b""
        """Data RECEIVED from the client."""
        self._client_send_buf = b""
        """Data to SEND to the client."""
        self._client_writable: bool = False
        """If the client is ready to accept data."""
        self._state = _WorkerState.CREATED
        self._ev_server = ev_server
        self._http_header_parser = HTTPHeaderParser()
        self._http_body_parser: HTTPBodyParser = None
        self._cur_session: _WorkerSession = None
        self._bfc = bfc

    def start(self):
        # Nothing to do here
        _log.debug("Worker starts on (%s:%s) <-> (%s:%s)", *self._bindings())

    def events(self):
        yield self._client_socket.fileno(
        ), select.EPOLLIN | select.EPOLLOUT | select.EPOLLET

    def terminate(self):
        """Abort and do the cleanups."""
        _log.debug("Terminating worker on (%s:%s) <-> (%s:%s)",
                   *self._bindings())

        self._ev_server.unregister(self)
        if self._cur_session:
            self._cur_session.end()
        self._client_socket.close()

    def queue_send(self, s: bytes = b"") -> int:
        """
        Try to send data to the client. Returns the number of bytes sent. All data will be queued for sending later
        nonetheless.
        return: Number of bytes sent.
        """
        total_byte_sent = 0
        self._client_send_buf += s
        try:
            while self._client_send_buf and self._client_writable:
                byte_sent = self._client_socket.send(self._client_send_buf)
                total_byte_sent += byte_sent
                self._client_send_buf = self._client_send_buf[byte_sent:]
        except socket.error as e:
            if e.errno != errno.EAGAIN:
                raise e
            else:
                self._client_writable = False
        return total_byte_sent

    def _bindings(self) -> List[Union[str, int]]:
        addr = self._client_socket.getsockname()
        return [
            addr[0], addr[1], self._client_address[0], self._client_address[1]
        ]

    def _handle_created(self, ev: int):
        """Event handler for state == CREATED"""
        if not ev & select.EPOLLIN:
            # We care only when we have something to parse.
            return
        ref_client_recv_buf = Ref(self._client_recv_buf)
        parse_result = self._http_header_parser.feed(ref_client_recv_buf)
        self._client_recv_buf = ref_client_recv_buf.v

        if parse_result == HTTPParseStatus.PARTIAL:
            return
        elif parse_result == HTTPParseStatus.ERROR:
            _log.debug("Worker got illegal input on (%s:%s) <-> (%s:%s)",
                       *self._bindings())
            self.terminate()
        elif isinstance(parse_result, HTTPRequestHeader):
            header = parse_result
            print('FINDME2', header.location)
            if header.method != b"CONNECT":
                # if we are not doing HTTP CONNECT, then the header consumed is also a part of the request. (after
                # conversion to non-proxy header)
                header.unproxify()
                self._client_recv_buf = header.reconstruct(
                ) + self._client_recv_buf
            else:
                self._client_send_buf = b'HTTP/1.1 200 OK\r\n\r\n'

            # This is the best we can do. Because HTTPS should use CONNECT anyway.
            port = header.location.port if header.location.port else 80
            self._state = _WorkerState.RELAYING
            self._http_body_parser = HTTPBodyParser(header)
            self._cur_session = _WorkerSession(
                (header.location.hostname, port), self, self._bfc)
            self._cur_session.start()
            self._handle_relaying(ev)

    def _handle_relaying(self, ev: int):
        """Event handler for state == RELAYING"""
        if ev & select.EPOLLIN:
            # I got some data from the client, I want to send it to the BFC.
            # but first, I need to determine if all the data belong to the current session.
            parse_result = self._http_body_parser.feed(self._client_recv_buf)
            if parse_result == HTTPParseStatus.PARTIAL:
                print('FINEME3', self._client_recv_buf)
                self._cur_session.send(self._client_recv_buf)
                self._client_recv_buf = b""
            elif parse_result == HTTPParseStatus.ERROR:
                self.terminate()
            elif isinstance(parse_result, int):
                # part of the buffer is the tail of the current session.
                self._cur_session.send(self._client_recv_buf[:parse_result])
                # the rest belongs to the coming new session.
                self._client_recv_buf = self._client_recv_buf[parse_result:]

                self._cur_session.end()
                self._state = _WorkerState.CREATED
                self._handle_created(ev)
        if ev & select.EPOLLOUT:
            # I can write to the client, try to send the buffer I have
            self.queue_send()

    def handle_event(self, fileno: int, ev: int):
        if ev & select.EPOLLIN:
            try:
                while True:
                    r = self._client_socket.recv(1024)
                    if not r:
                        # died
                        self.terminate()
                        break
                    self._client_recv_buf += r
            except socket.error as e:
                if e.errno != errno.EAGAIN:
                    raise e
            print('FINDME', self._client_recv_buf)

        if ev & select.EPOLLOUT:
            self._client_writable = True

        if self._state == _WorkerState.CREATED:
            self._handle_created(ev)
        elif self._state == _WorkerState.RELAYING:
            self._handle_relaying(ev)
예제 #4
0
def test_header_cut_input():
    parser = HTTPHeaderParser()
    ref = Ref(HTTP_10_GET() + b"extra data")
    rst = parser.feed(ref)
    assert isinstance(rst, HTTPRequestHeader)
    assert ref.v == b'extra data'
예제 #5
0
def test_header_partial_input(s):
    parser = HTTPHeaderParser()
    rst = parser.feed(Ref(s))
    assert isinstance(rst, HTTPParseStatus)
    assert rst == HTTPParseStatus.PARTIAL
예제 #6
0
def test_header_bad_input():
    parser = HTTPHeaderParser()
    rst = parser.feed(Ref(b"a b c d e f g"))
    assert isinstance(rst, HTTPParseStatus)
    assert rst == HTTPParseStatus.ERROR
예제 #7
0
def test_unproxyify():
    rst = HTTPHeaderParser().feed(Ref(HTTP_10_GET()))
    rst.unproxify()
    assert rst.headers[b'Host'][1] == b'example.com'
    assert rst.uri == b'/path?key=value'
예제 #8
0
def test_reconstruct_header():
    s = b"GET http://example.com/path?key=value HTTP/1.0\r\n2Header: header-val1\r\n1Header: header-val1\r\n\r\n"
    assert HTTPHeaderParser().feed(Ref(s)).reconstruct() == s