mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-26 18:18:25 +00:00
[sans-io] http: emit error hook when killed
This commit is contained in:
parent
7087539c4d
commit
4f0cbec308
@ -183,7 +183,7 @@ class HttpStream(layer.Layer):
|
|||||||
self.flow.request.host_header = self.context.server.address[0]
|
self.flow.request.host_header = self.context.server.address[0]
|
||||||
|
|
||||||
yield HttpRequestHeadersHook(self.flow)
|
yield HttpRequestHeadersHook(self.flow)
|
||||||
if (yield from self.check_killed()):
|
if (yield from self.check_killed(True)):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.flow.request.headers.get("expect", "").lower() == "100-continue":
|
if self.flow.request.headers.get("expect", "").lower() == "100-continue":
|
||||||
@ -227,13 +227,13 @@ class HttpStream(layer.Layer):
|
|||||||
self.flow.request.data.content = self.request_body_buf
|
self.flow.request.data.content = self.request_body_buf
|
||||||
self.request_body_buf = b""
|
self.request_body_buf = b""
|
||||||
yield HttpRequestHook(self.flow)
|
yield HttpRequestHook(self.flow)
|
||||||
if (yield from self.check_killed()):
|
if (yield from self.check_killed(True)):
|
||||||
return
|
return
|
||||||
elif self.flow.response:
|
elif self.flow.response:
|
||||||
# response was set by an inline script.
|
# response was set by an inline script.
|
||||||
# we now need to emulate the responseheaders hook.
|
# we now need to emulate the responseheaders hook.
|
||||||
yield HttpResponseHeadersHook(self.flow)
|
yield HttpResponseHeadersHook(self.flow)
|
||||||
if (yield from self.check_killed()):
|
if (yield from self.check_killed(True)):
|
||||||
return
|
return
|
||||||
yield from self.send_response()
|
yield from self.send_response()
|
||||||
else:
|
else:
|
||||||
@ -253,7 +253,7 @@ class HttpStream(layer.Layer):
|
|||||||
def state_wait_for_response_headers(self, event: ResponseHeaders) -> layer.CommandGenerator[None]:
|
def state_wait_for_response_headers(self, event: ResponseHeaders) -> layer.CommandGenerator[None]:
|
||||||
self.flow.response = event.response
|
self.flow.response = event.response
|
||||||
yield HttpResponseHeadersHook(self.flow)
|
yield HttpResponseHeadersHook(self.flow)
|
||||||
if (yield from self.check_killed()):
|
if (yield from self.check_killed(True)):
|
||||||
return
|
return
|
||||||
elif self.flow.response.stream:
|
elif self.flow.response.stream:
|
||||||
yield SendHttp(event, self.context.client)
|
yield SendHttp(event, self.context.client)
|
||||||
@ -286,23 +286,9 @@ class HttpStream(layer.Layer):
|
|||||||
yield from self.send_response()
|
yield from self.send_response()
|
||||||
self.server_state = self.state_done
|
self.server_state = self.state_done
|
||||||
|
|
||||||
def check_killed(self) -> layer.CommandGenerator[bool]:
|
|
||||||
killed_by_us = (
|
|
||||||
self.flow.error and self.flow.error.msg == flow.Error.KILLED_MESSAGE
|
|
||||||
)
|
|
||||||
killed_by_remote = (
|
|
||||||
self.context.client.state is not ConnectionState.OPEN
|
|
||||||
)
|
|
||||||
if killed_by_us or killed_by_remote:
|
|
||||||
if self.context.client.state & ConnectionState.CAN_WRITE:
|
|
||||||
yield commands.CloseConnection(self.context.client)
|
|
||||||
self._handle_event = self.state_errored
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def send_response(self):
|
def send_response(self):
|
||||||
yield HttpResponseHook(self.flow)
|
yield HttpResponseHook(self.flow)
|
||||||
if (yield from self.check_killed()):
|
if (yield from self.check_killed(False)):
|
||||||
return
|
return
|
||||||
has_content = bool(self.flow.response.raw_content)
|
has_content = bool(self.flow.response.raw_content)
|
||||||
yield SendHttp(ResponseHeaders(self.stream_id, self.flow.response, not has_content), self.context.client)
|
yield SendHttp(ResponseHeaders(self.stream_id, self.flow.response, not has_content), self.context.client)
|
||||||
@ -310,6 +296,25 @@ class HttpStream(layer.Layer):
|
|||||||
yield SendHttp(ResponseData(self.stream_id, self.flow.response.raw_content), self.context.client)
|
yield SendHttp(ResponseData(self.stream_id, self.flow.response.raw_content), self.context.client)
|
||||||
yield SendHttp(ResponseEndOfMessage(self.stream_id), self.context.client)
|
yield SendHttp(ResponseEndOfMessage(self.stream_id), self.context.client)
|
||||||
|
|
||||||
|
def check_killed(self, emit_error_hook: bool) -> layer.CommandGenerator[bool]:
|
||||||
|
killed_by_us = (
|
||||||
|
self.flow.error and self.flow.error.msg == flow.Error.KILLED_MESSAGE
|
||||||
|
)
|
||||||
|
killed_by_remote = (
|
||||||
|
self.context.client.state is not ConnectionState.OPEN
|
||||||
|
)
|
||||||
|
if killed_by_remote:
|
||||||
|
if not self.flow.error:
|
||||||
|
self.flow.error = flow.Error("Client disconnected.")
|
||||||
|
if killed_by_us or killed_by_remote:
|
||||||
|
if emit_error_hook:
|
||||||
|
yield HttpErrorHook(self.flow)
|
||||||
|
if self.context.client.state & ConnectionState.CAN_WRITE:
|
||||||
|
yield commands.CloseConnection(self.context.client)
|
||||||
|
self._handle_event = self.state_errored
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def handle_protocol_error(
|
def handle_protocol_error(
|
||||||
self,
|
self,
|
||||||
event: typing.Union[RequestProtocolError, ResponseProtocolError]
|
event: typing.Union[RequestProtocolError, ResponseProtocolError]
|
||||||
@ -331,7 +336,7 @@ class HttpStream(layer.Layer):
|
|||||||
self.flow.error = flow.Error(event.message)
|
self.flow.error = flow.Error(event.message)
|
||||||
yield HttpErrorHook(self.flow)
|
yield HttpErrorHook(self.flow)
|
||||||
|
|
||||||
if (yield from self.check_killed()):
|
if (yield from self.check_killed(False)):
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(event, ResponseProtocolError):
|
if isinstance(event, ResponseProtocolError):
|
||||||
@ -353,7 +358,7 @@ class HttpStream(layer.Layer):
|
|||||||
|
|
||||||
def handle_connect(self) -> layer.CommandGenerator[None]:
|
def handle_connect(self) -> layer.CommandGenerator[None]:
|
||||||
yield HttpConnectHook(self.flow)
|
yield HttpConnectHook(self.flow)
|
||||||
if (yield from self.check_killed()):
|
if (yield from self.check_killed(False)):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.context.server.address = (self.flow.request.host, self.flow.request.port)
|
self.context.server.address = (self.flow.request.host, self.flow.request.port)
|
||||||
|
@ -44,6 +44,8 @@ class HttpErrorHook(commands.Hook):
|
|||||||
An HTTP error has occurred, e.g. invalid server responses, or
|
An HTTP error has occurred, e.g. invalid server responses, or
|
||||||
interrupted connections. This is distinct from a valid server HTTP
|
interrupted connections. This is distinct from a valid server HTTP
|
||||||
error response, which is simply a response with an HTTP error code.
|
error response, which is simply a response with an HTTP error code.
|
||||||
|
|
||||||
|
Every flow will receive either an error or an response event, but not both.
|
||||||
"""
|
"""
|
||||||
name = "error"
|
name = "error"
|
||||||
flow: http.HTTPFlow
|
flow: http.HTTPFlow
|
||||||
|
@ -806,19 +806,23 @@ def test_kill_flow(tctx, when):
|
|||||||
flow = Placeholder(HTTPFlow)
|
flow = Placeholder(HTTPFlow)
|
||||||
|
|
||||||
def kill(flow: HTTPFlow):
|
def kill(flow: HTTPFlow):
|
||||||
|
# Can't use flow.kill() here because that currently still depends on a reply object.
|
||||||
flow.error = Error(Error.KILLED_MESSAGE)
|
flow.error = Error(Error.KILLED_MESSAGE)
|
||||||
|
|
||||||
def assert_kill():
|
def assert_kill(err_hook: bool = True):
|
||||||
assert (playbook
|
playbook >> reply(side_effect=kill)
|
||||||
>> reply(side_effect=kill)
|
if err_hook:
|
||||||
<< CloseConnection(tctx.client))
|
playbook << http.HttpErrorHook(flow)
|
||||||
|
playbook >> reply()
|
||||||
|
playbook << CloseConnection(tctx.client)
|
||||||
|
assert playbook
|
||||||
|
|
||||||
playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))
|
playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))
|
||||||
assert (playbook
|
assert (playbook
|
||||||
>> DataReceived(tctx.client, b"CONNECT example.com:80 HTTP/1.1\r\n\r\n")
|
>> DataReceived(tctx.client, b"CONNECT example.com:80 HTTP/1.1\r\n\r\n")
|
||||||
<< http.HttpConnectHook(connect_flow))
|
<< http.HttpConnectHook(connect_flow))
|
||||||
if when == "http_connect":
|
if when == "http_connect":
|
||||||
return assert_kill()
|
return assert_kill(False)
|
||||||
assert (playbook
|
assert (playbook
|
||||||
>> reply()
|
>> reply()
|
||||||
<< SendData(tctx.client, b'HTTP/1.1 200 Connection established\r\n\r\n')
|
<< SendData(tctx.client, b'HTTP/1.1 200 Connection established\r\n\r\n')
|
||||||
@ -853,14 +857,14 @@ def test_kill_flow(tctx, when):
|
|||||||
>> reply()
|
>> reply()
|
||||||
>> DataReceived(server, b"!")
|
>> DataReceived(server, b"!")
|
||||||
<< http.HttpResponseHook(flow))
|
<< http.HttpResponseHook(flow))
|
||||||
return assert_kill()
|
return assert_kill(False)
|
||||||
elif when == "error":
|
elif when == "error":
|
||||||
assert (playbook
|
assert (playbook
|
||||||
>> reply()
|
>> reply()
|
||||||
>> ConnectionClosed(server)
|
>> ConnectionClosed(server)
|
||||||
<< CloseConnection(server)
|
<< CloseConnection(server)
|
||||||
<< http.HttpErrorHook(flow))
|
<< http.HttpErrorHook(flow))
|
||||||
return assert_kill()
|
return assert_kill(False)
|
||||||
else:
|
else:
|
||||||
raise AssertionError
|
raise AssertionError
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user