def __call__(self): if self.mode == "transparent": self.__initial_server_tls = self.server_tls self.__initial_server_conn = self.server_conn while True: try: request = self.get_request_from_client() # Make sure that the incoming request matches our expectations self.validate_request(request) except netlib.exceptions.HttpReadDisconnect: # don't throw an error for disconnects that happen before/between requests. return except netlib.exceptions.HttpException as e: # We optimistically guess there might be an HTTP client on the # other end self.send_error_response(400, repr(e)) six.reraise( exceptions.ProtocolException, exceptions.ProtocolException( "HTTP protocol error in client request: {}".format(e) ), sys.exc_info()[2] ) self.log("request", "debug", [repr(request)]) # Handle Proxy Authentication # Proxy Authentication conceptually does not work in transparent mode. # We catch this misconfiguration on startup. Here, we sort out requests # after a successful CONNECT request (which do not need to be validated anymore) if not (self.http_authenticated or self.authenticate(request)): return flow = models.HTTPFlow(self.client_conn, self.server_conn, live=self) flow.request = request try: # Regular Proxy Mode: Handle CONNECT if self.mode == "regular" and request.first_line_format == "authority": self.handle_regular_mode_connect(request) return except (exceptions.ProtocolException, netlib.exceptions.NetlibException) as e: # HTTPS tasting means that ordinary errors like resolution and # connection errors can happen here. self.send_error_response(502, repr(e)) flow.error = models.Error(str(e)) self.channel.ask("error", flow) return # update host header in reverse proxy mode if self.config.options.mode == "reverse": if six.PY2: flow.request.headers["Host"] = self.config.upstream_server.address.host.encode() else: flow.request.headers["Host"] = self.config.upstream_server.address.host # set upstream auth if self.mode == "upstream" and self.config.upstream_auth is not None: flow.request.headers["Proxy-Authorization"] = self.config.upstream_auth self.process_request_hook(flow) try: if websockets.check_handshake(request.headers) and websockets.check_client_version(request.headers): # We only support RFC6455 with WebSockets version 13 # allow inline scripts to manipulate the client handshake self.channel.ask("websockets_handshake", flow) if not flow.response: self.establish_server_connection( flow.request.host, flow.request.port, flow.request.scheme ) self.get_response_from_server(flow) else: # response was set by an inline script. # we now need to emulate the responseheaders hook. flow = self.channel.ask("responseheaders", flow) self.log("response", "debug", [repr(flow.response)]) flow = self.channel.ask("response", flow) self.send_response_to_client(flow) if self.check_close_connection(flow): return # Handle 101 Switching Protocols if flow.response.status_code == 101: return self.handle_101_switching_protocols(flow) # Upstream Proxy Mode: Handle CONNECT if flow.request.first_line_format == "authority" and flow.response.status_code == 200: self.handle_upstream_mode_connect(flow.request.copy()) return except (exceptions.ProtocolException, netlib.exceptions.NetlibException) as e: self.send_error_response(502, repr(e)) if not flow.response: flow.error = models.Error(str(e)) self.channel.ask("error", flow) return else: six.reraise(exceptions.ProtocolException, exceptions.ProtocolException( "Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2]) finally: if flow: flow.live = False
def __call__(self): if self.mode == "transparent": self.__initial_server_tls = self.server_tls self.__initial_server_conn = self.server_conn while True: try: request = self.get_request_from_client() # Make sure that the incoming request matches our expectations self.validate_request(request) except netlib.exceptions.HttpReadDisconnect: # don't throw an error for disconnects that happen before/between requests. return except netlib.exceptions.HttpException as e: # We optimistically guess there might be an HTTP client on the # other end self.send_error_response(400, repr(e)) self.log( "request", "warn", "HTTP protocol error in client request: %s" % e ) return self.log("request", "debug", [repr(request)]) # Handle Proxy Authentication # Proxy Authentication conceptually does not work in transparent mode. # We catch this misconfiguration on startup. Here, we sort out requests # after a successful CONNECT request (which do not need to be validated anymore) if not (self.http_authenticated or self.authenticate(request)): return flow = models.HTTPFlow(self.client_conn, self.server_conn, live=self) flow.request = request try: # Regular Proxy Mode: Handle CONNECT if self.mode == "regular" and request.first_line_format == "authority": self.handle_regular_mode_connect(request) return except (exceptions.ProtocolException, netlib.exceptions.NetlibException) as e: # HTTPS tasting means that ordinary errors like resolution and # connection errors can happen here. self.send_error_response(502, repr(e)) flow.error = models.Error(str(e)) self.channel.ask("error", flow) return # set upstream auth if self.mode == "upstream" and self.config.upstream_auth is not None: flow.request.headers["Proxy-Authorization"] = self.config.upstream_auth self.process_request_hook(flow) try: if websockets.check_handshake(request.headers) and websockets.check_client_version(request.headers): # we only support RFC6455 with WebSockets version 13 # allow inline scripts to manupulate the client handshake self.channel.ask("websockets_handshake", flow) if not flow.response: self.establish_server_connection( flow.request.host, flow.request.port, flow.request.scheme ) self.get_response_from_server(flow) else: # response was set by an inline script. # we now need to emulate the responseheaders hook. flow = self.channel.ask("responseheaders", flow) self.log("response", "debug", [repr(flow.response)]) flow = self.channel.ask("response", flow) self.send_response_to_client(flow) if self.check_close_connection(flow): return # Handle 101 Switching Protocols # It may be useful to pass additional args (such as the upgrade header) # to next_layer in the future if flow.response.status_code == 101: layer = self.ctx.next_layer(self, flow) layer() return # Upstream Proxy Mode: Handle CONNECT if flow.request.first_line_format == "authority" and flow.response.status_code == 200: self.handle_upstream_mode_connect(flow.request.copy()) return except (exceptions.ProtocolException, netlib.exceptions.NetlibException) as e: self.send_error_response(502, repr(e)) if not flow.response: flow.error = models.Error(str(e)) self.channel.ask("error", flow) return else: six.reraise(exceptions.ProtocolException, exceptions.ProtocolException( "Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2]) finally: if flow: flow.live = False
def test_check_client_version(self, input, expected): h = http.Headers(input) assert websockets.check_client_version(h) == expected