Skip to content

Commit 5af57cf

Browse files
committed
HTTPRequest -> http.Request, add request.authority
1 parent 2dfcb53 commit 5af57cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1058
-1027
lines changed

examples/addons/duplicate-modify-replay.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
def request(flow):
66
# Avoid an infinite loop by not replaying already replayed requests
7-
if flow.request.is_replay:
7+
if flow.is_replay == "request":
88
return
99
flow = flow.copy()
1010
# Only interactive tools have a view. If we have one, add a duplicate entry

mitmproxy/addons/clientplayback.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from mitmproxy.coretypes import basethread
1818
from mitmproxy.net import server_spec, tls
1919
from mitmproxy.net.http import http1
20+
from mitmproxy.net.http.url import hostport
2021
from mitmproxy.utils import human
2122

2223

@@ -46,7 +47,7 @@ def replay(self, f): # pragma: no cover
4647
f.live = True
4748
r = f.request
4849
bsl = human.parse_size(self.options.body_size_limit)
49-
first_line_format_backup = r.first_line_format
50+
authority_backup = r.authority
5051
server = None
5152
try:
5253
f.response = None
@@ -79,9 +80,9 @@ def replay(self, f): # pragma: no cover
7980
sni=f.server_conn.sni,
8081
**tls.client_arguments_from_options(self.options)
8182
)
82-
r.first_line_format = "relative"
83+
r.authority = b""
8384
else:
84-
r.first_line_format = "absolute"
85+
r.authority = hostport(r.scheme, r.host, r.port)
8586
else:
8687
server_address = (r.host, r.port)
8788
server = connections.ServerConnection(server_address)
@@ -91,7 +92,7 @@ def replay(self, f): # pragma: no cover
9192
sni=f.server_conn.sni,
9293
**tls.client_arguments_from_options(self.options)
9394
)
94-
r.first_line_format = "relative"
95+
r.authority = ""
9596

9697
server.wfile.write(http1.assemble_request(r))
9798
server.wfile.flush()
@@ -101,9 +102,7 @@ def replay(self, f): # pragma: no cover
101102
f.server_conn.close()
102103
f.server_conn = server
103104

104-
f.response = http.HTTPResponse.wrap(
105-
http1.read_response(server.rfile, r, body_size_limit=bsl)
106-
)
105+
f.response = http1.read_response(server.rfile, r, body_size_limit=bsl)
107106
response_reply = self.channel.ask("response", f)
108107
if response_reply == exceptions.Kill:
109108
raise exceptions.Kill()
@@ -115,7 +114,7 @@ def replay(self, f): # pragma: no cover
115114
except Exception as e:
116115
self.channel.tell("log", log.LogEntry(repr(e), "error"))
117116
finally:
118-
r.first_line_format = first_line_format_backup
117+
r.authority = authority_backup
119118
f.live = False
120119
if server and server.connected():
121120
server.finish()
@@ -200,7 +199,7 @@ def start_replay(self, flows: typing.Sequence[flow.Flow]) -> None:
200199
lst.append(hf)
201200
# Prepare the flow for replay
202201
hf.backup()
203-
hf.request.is_replay = True
202+
hf.is_replay = "request"
204203
hf.response = None
205204
hf.error = None
206205
# https://github.com/mitmproxy/mitmproxy/issues/2197

mitmproxy/addons/dumper.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def _echo_request_line(self, flow):
127127
human.format_address(flow.client_conn.address)
128128
)
129129
)
130-
elif flow.request.is_replay:
130+
elif flow.is_replay == "request":
131131
client = click.style("[replay]", fg="yellow", bold=True)
132132
else:
133133
client = ""
@@ -166,7 +166,7 @@ def _echo_request_line(self, flow):
166166
self.echo(line)
167167

168168
def _echo_response_line(self, flow):
169-
if flow.response.is_replay:
169+
if flow.is_replay == "response":
170170
replay = click.style("[replay] ", fg="yellow", bold=True)
171171
else:
172172
replay = ""

mitmproxy/addons/intercept.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def process_flow(self, f):
3737
if self.filt:
3838
should_intercept = all([
3939
self.filt(f),
40-
not f.request.is_replay,
40+
not f.is_replay == "request",
4141
])
4242
if should_intercept and ctx.options.intercept_active:
4343
f.intercept()

mitmproxy/addons/mapremote.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ def request(self, flow: http.HTTPFlow) -> None:
4747
# this is a bit messy: setting .url also updates the host header,
4848
# so we really only do that if the replacement affected the URL.
4949
if url != new_url:
50-
flow.request.url = new_url
50+
flow.request.url = new_url # type: ignore

mitmproxy/addons/serverplayback.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,10 @@ def request(self, f: http.HTTPFlow) -> None:
202202
if rflow:
203203
assert rflow.response
204204
response = rflow.response.copy()
205-
response.is_replay = True
206205
if ctx.options.server_replay_refresh:
207206
response.refresh()
208207
f.response = response
208+
f.is_replay = "response"
209209
elif ctx.options.server_replay_kill_extra:
210210
ctx.log.warn(
211211
"server_playback: killed non-replay request {}".format(

mitmproxy/coretypes/serializable.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import abc
22
import uuid
3+
from typing import Type, TypeVar
4+
5+
T = TypeVar('T', bound='Serializable')
36

47

58
class Serializable(metaclass=abc.ABCMeta):
@@ -9,7 +12,7 @@ class Serializable(metaclass=abc.ABCMeta):
912

1013
@classmethod
1114
@abc.abstractmethod
12-
def from_state(cls, state):
15+
def from_state(cls: Type[T], state) -> T:
1316
"""
1417
Create a new object from the given state.
1518
"""
@@ -29,7 +32,7 @@ def set_state(self, state):
2932
"""
3033
raise NotImplementedError()
3134

32-
def copy(self):
35+
def copy(self: T) -> T:
3336
state = self.get_state()
3437
if isinstance(state, dict) and "id" in state:
3538
state["id"] = str(uuid.uuid4())

mitmproxy/flow.py

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def __init__(
7777
self._backup: typing.Optional[Flow] = None
7878
self.reply: typing.Optional[controller.Reply] = None
7979
self.marked: bool = False
80+
self.is_replay: typing.Optional[str] = None
8081
self.metadata: typing.Dict[str, typing.Any] = dict()
8182

8283
_stateobject_attributes = dict(
@@ -86,6 +87,7 @@ def __init__(
8687
server_conn=connections.ServerConnection,
8788
type=str,
8889
intercepted=bool,
90+
is_replay=str,
8991
marked=bool,
9092
metadata=typing.Dict[str, typing.Any],
9193
)

mitmproxy/http.py

+25-149
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,13 @@
11
import html
2-
from typing import Optional
3-
2+
import time
3+
from typing import Optional, Tuple
44
from mitmproxy import connections
55
from mitmproxy import flow
66
from mitmproxy import version
77
from mitmproxy.net import http
88

9-
10-
class HTTPRequest(http.Request):
11-
"""
12-
A mitmproxy HTTP request.
13-
"""
14-
15-
# This is a very thin wrapper on top of :py:class:`mitmproxy.net.http.Request` and
16-
# may be removed in the future.
17-
18-
def __init__(
19-
self,
20-
first_line_format,
21-
method,
22-
scheme,
23-
host,
24-
port,
25-
path,
26-
http_version,
27-
headers,
28-
content,
29-
trailers=None,
30-
timestamp_start=None,
31-
timestamp_end=None,
32-
is_replay=False,
33-
):
34-
http.Request.__init__(
35-
self,
36-
first_line_format,
37-
method,
38-
scheme,
39-
host,
40-
port,
41-
path,
42-
http_version,
43-
headers,
44-
content,
45-
trailers,
46-
timestamp_start,
47-
timestamp_end,
48-
)
49-
# Is this request replayed?
50-
self.is_replay = is_replay
51-
self.stream = None
52-
53-
def get_state(self):
54-
state = super().get_state()
55-
state["is_replay"] = self.is_replay
56-
return state
57-
58-
def set_state(self, state):
59-
state = state.copy()
60-
self.is_replay = state.pop("is_replay")
61-
super().set_state(state)
62-
63-
@classmethod
64-
def wrap(self, request):
65-
"""
66-
Wraps an existing :py:class:`mitmproxy.net.http.Request`.
67-
"""
68-
req = HTTPRequest(
69-
first_line_format=request.data.first_line_format,
70-
method=request.data.method,
71-
scheme=request.data.scheme,
72-
host=request.data.host,
73-
port=request.data.port,
74-
path=request.data.path,
75-
http_version=request.data.http_version,
76-
headers=request.data.headers,
77-
content=request.data.content,
78-
trailers=request.data.trailers,
79-
timestamp_start=request.data.timestamp_start,
80-
timestamp_end=request.data.timestamp_end,
81-
)
82-
return req
83-
84-
def __hash__(self):
85-
return id(self)
86-
87-
88-
class HTTPResponse(http.Response):
89-
"""
90-
A mitmproxy HTTP response.
91-
"""
92-
93-
# This is a very thin wrapper on top of :py:class:`mitmproxy.net.http.Response` and
94-
# may be removed in the future.
95-
96-
def __init__(
97-
self,
98-
http_version,
99-
status_code,
100-
reason,
101-
headers,
102-
content,
103-
trailers=None,
104-
timestamp_start=None,
105-
timestamp_end=None,
106-
is_replay=False
107-
):
108-
http.Response.__init__(
109-
self,
110-
http_version,
111-
status_code,
112-
reason,
113-
headers,
114-
content,
115-
trailers,
116-
timestamp_start=timestamp_start,
117-
timestamp_end=timestamp_end,
118-
)
119-
120-
# Is this request replayed?
121-
self.is_replay = is_replay
122-
self.stream = None
123-
124-
@classmethod
125-
def wrap(self, response):
126-
"""
127-
Wraps an existing :py:class:`mitmproxy.net.http.Response`.
128-
"""
129-
resp = HTTPResponse(
130-
http_version=response.data.http_version,
131-
status_code=response.data.status_code,
132-
reason=response.data.reason,
133-
headers=response.data.headers,
134-
content=response.data.content,
135-
trailers=response.data.trailers,
136-
timestamp_start=response.data.timestamp_start,
137-
timestamp_end=response.data.timestamp_end,
138-
)
139-
return resp
9+
HTTPRequest = http.Request
10+
HTTPResponse = http.Response
14011

14112

14213
class HTTPFlow(flow.Flow):
@@ -197,8 +68,7 @@ def make_error_response(
19768
message: str = "",
19869
headers: Optional[http.Headers] = None,
19970
) -> HTTPResponse:
200-
reason = http.status_codes.RESPONSES.get(status_code, "Unknown")
201-
body = """
71+
body: bytes = """
20272
<html>
20373
<head>
20474
<title>{status_code} {reason}</title>
@@ -210,7 +80,7 @@ def make_error_response(
21080
</html>
21181
""".strip().format(
21282
status_code=status_code,
213-
reason=reason,
83+
reason=http.status_codes.RESPONSES.get(status_code, "Unknown"),
21484
message=html.escape(message),
21585
).encode("utf8", "replace")
21686

@@ -222,19 +92,23 @@ def make_error_response(
22292
Content_Type="text/html"
22393
)
22494

225-
return HTTPResponse(
226-
b"HTTP/1.1",
227-
status_code,
228-
reason,
229-
headers,
230-
body,
231-
)
95+
return HTTPResponse.make(status_code, body, headers)
23296

23397

234-
def make_connect_request(address):
98+
def make_connect_request(address: Tuple[str, int]) -> HTTPRequest:
23599
return HTTPRequest(
236-
"authority", b"CONNECT", None, address[0], address[1], None, b"HTTP/1.1",
237-
http.Headers(), b""
100+
host=address[0],
101+
port=address[1],
102+
method=b"CONNECT",
103+
scheme=b"",
104+
authority=f"{address[0]}:{address[1]}".encode(),
105+
path=b"",
106+
http_version=b"HTTP/1.1",
107+
headers=http.Headers(),
108+
content=b"",
109+
trailers=None,
110+
timestamp_start=time.time(),
111+
timestamp_end=time.time(),
238112
)
239113

240114

@@ -247,9 +121,11 @@ def make_connect_response(http_version):
247121
b"Connection established",
248122
http.Headers(),
249123
b"",
124+
None,
125+
time.time(),
126+
time.time(),
250127
)
251128

252129

253-
expect_continue_response = HTTPResponse(
254-
b"HTTP/1.1", 100, b"Continue", http.Headers(), b""
255-
)
130+
def make_expect_continue_response():
131+
return HTTPResponse.make(100)

0 commit comments

Comments
 (0)