Exemplo n.º 1
0
class HttpProtocolHandler:

    PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT = build_http_response(
        200, reason=b'Connection established')

    def __init__(self, client_conn: socket.socket, client_addr, flags, req):
        self.start_time: float = time.time()

        self.client = client_conn
        self.req = req

        self.client_addr = client_addr  # host and socket_fd
        self.flags = flags

        self.request_parser = HttpParser(0)  # 0 - parse only requests
        self.response_parser = HttpParser(1)  # 1 - parse only responses
        self.total_response_size: int = 0

        self.upstream: Optional[urlparse.SplitResultBytes] = None
        self.host = None
        self.port = None
        self.upstream_url = None

        self.server: Optional[socket.socket] = None

    def parse_url(self, parser):
        url = parser.get_url()
        method = parser.get_method()

        protocol_pos = url.find('://')
        if protocol_pos != -1:
            url = url[(protocol_pos + 3):]

        port_pos = url.find(':')
        host_pos = url.find('/')
        if host_pos == -1:
            host_pos = len(url)
        if port_pos == -1 or host_pos < port_pos:
            port = 443 if method == "CONNECT" else DEFAULT_HTTP_PORT
        else:
            port = int((url[port_pos + 1:])[:host_pos - port_pos - 1])

        port_ind = url.find(':')
        if port_ind != -1:
            url = url[:port_ind]

        self.upstream = urlparse.urlsplit('http://' + url + '/')
        self.upstream_url = self.build_upstream_relative_path()
        host = self.upstream.hostname

        port_ind = host.find(':')
        if port_ind != -1:
            host = host[:port_ind]

        return host, port

    def run(self) -> None:
        p = HttpParser()
        try:
            p.execute(self.req, len(self.req))

            url = p.get_url()
            metopd = p.get_method()

            http_pos = url.find('://')
            if http_pos == -1:
                temp = url
            else:
                temp = url[(http_pos + 3):]

            port_pos = temp.find(':')
            host_pos = temp.find('/')
            if host_pos == -1:
                host_pos = len(temp)
            if port_pos == -1 or host_pos < port_pos:
                port = 443 if metopd == "CONNECT" else 80
            else:
                port = int((temp[port_pos + 1:])[:host_pos - port_pos - 1])

            host = p.get_headers()['host']
            port_ind = host.find(':')
            if port_ind != -1:
                host = host[:port_ind]
            if metopd == "CONNECT":
                https_proxy(host, port, self.client)
            else:
                proxy(host, port, self.client, self.req)
        except Exception as e:
            print(e)
            pass

    def access_log(self):
        server_host, server_port = self.upstream.hostname, self.upstream.port \
            if self.upstream.port else DEFAULT_HTTP_PORT

        connection_time_ms = (time.time() - self.start_time) * 1000
        method = self.request_parser.get_method()
        if method == httpMethods.CONNECT:
            pass
        elif method:
            print('pid:%s |  %s:%s - %s %s:%s%s - %s %s - %s bytes - %.2f ms' %
                  (str(getpid()), self.client_addr[0], self.client_addr[1],
                   method, server_host, server_port,
                   self.request_parser.get_path(),
                   self.response_parser.get_status_code(),
                   self.response_parser.get_errno(), self.total_response_size,
                   connection_time_ms))