Skip to content

Commit cd4dee4

Browse files
committed
feat: Add http_version, trailers, timestamp_start and timestamp_end fields
1 parent dfc891d commit cd4dee4

File tree

2 files changed

+68
-21
lines changed

2 files changed

+68
-21
lines changed

API.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,16 @@ Setting request data at this stage will affect the mitmproxy UI and later client
143143

144144
## Request
145145

146-
| Key | Value | Type | Optional? |
147-
|-----------|----------------------------------------|------------------------------|-----------|
148-
| `method` | The HTTP method (e.g. "GET" or "POST") | String | No |
149-
| `url` | The request URL | String (URI) | No |
150-
| `headers` | The request headers | Object(String: List(String)) | No |
151-
| `body` | The request body | String (base64) | No |
146+
| Key | Value | Type | Optional? |
147+
|-------------------|-------------------------------------------------------------------------------|------------------------------|-----------|
148+
| `http_version` | The HTTP version (e.g. "HTTP/1.1"). Can be left out to use a sensible option. | String | Yes |
149+
| `method` | The HTTP method (e.g. "GET" or "POST") | String | No |
150+
| `url` | The request URL | String (URI) | No |
151+
| `headers` | The request headers | Object(String: List(String)) | No |
152+
| `body` | The request body | String (base64) | No |
153+
| `trailers` | The request trailers | Object(String: List(String)) | Yes |
154+
| `timestamp_start` | The time of the start of the request (UNIX epoch time, seconds) | Number | |
155+
| `timestamp_end` | The time of the end of the request (UNIX epoch time, seconds) | Number | |
152156

153157
## Response summary
154158

@@ -159,9 +163,13 @@ Setting request data at this stage will affect the mitmproxy UI and later client
159163

160164
## Response
161165

162-
| Key | Value | Type | Optional? |
163-
|---------------|----------------------------|------------------------------|------------------------------------------------------------|
164-
| `status_code` | The response status code | Integer | No |
165-
| `reason` | The response reason phrase | String | Yes (a default value based on the status code may be used) |
166-
| `headers` | The response headers | Object(String: List(String)) | No |
167-
| `body` | The response body | String (base64) | No |
166+
| Key | Value | Type | Optional? |
167+
|-------------------|-------------------------------------------------------------------------------|------------------------------|------------------------------------------------------------|
168+
| `http_version` | The HTTP version (e.g. "HTTP/1.1"). Can be left out to use a sensible option. | String | Yes |
169+
| `status_code` | The response status code | Integer | No |
170+
| `reason` | The response reason phrase | String | Yes (a default value based on the status code may be used) |
171+
| `headers` | The response headers | Object(String: List(String)) | No |
172+
| `body` | The response body | String (base64) | No |
173+
| `trailers` | The response trailers | Object(String: List(String)) | Yes |
174+
| `timestamp_start` | The time of the start of the response (UNIX epoch time, seconds) | Number | |
175+
| `timestamp_end` | The time of the end of the response (UNIX epoch time, seconds) | Number | |

src/mitmproxy_remote_interceptions.py

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,14 @@ async def _handle_http_message(self, flow: http.HTTPFlow, is_request: bool):
144144

145145
# Use the received messages.
146146
if message_set.request is not None:
147+
if message_set.request.http_version == "RI/UNSET":
148+
message_set.request.http_version = flow.request.http_version \
149+
if flow.request is not None else "HTTP/1.1"
147150
flow.request = message_set.request
148151
if message_set.response is not None:
152+
if message_set.response.http_version == "RI/UNSET":
153+
message_set.response.http_version = flow.response.http_version \
154+
if flow.response is not None else "HTTP/1.1"
149155
flow.response = message_set.response
150156

151157

@@ -167,10 +173,14 @@ def _headers_from_json(headers_json: dict[str, list[str]]) -> http.Headers:
167173

168174
def _request_to_json(request: http.Request) -> dict[str, object]:
169175
return {
176+
"http_version": request.http_version,
170177
"method": request.method,
171178
"url": request.url,
172179
"headers": _headers_to_json(request.headers),
173180
"body": base64.b64encode(request.get_content(strict=False)).decode("utf-8"),
181+
"trailers": _headers_to_json(request.trailers) if request.trailers else None,
182+
"timestamp_start": request.timestamp_start,
183+
"timestamp_end": request.timestamp_end,
174184
}
175185

176186

@@ -182,20 +192,41 @@ def _request_to_summary_json(request: http.Request) -> dict[str, object]:
182192

183193

184194
def _request_from_json(request_json: dict[str, object]) -> http.Request:
185-
return http.Request.make(
186-
method=typing.cast(str, request_json["method"]),
187-
url=typing.cast(str, request_json["url"]),
188-
content=base64.b64decode(typing.cast(str, request_json["body"])),
195+
request = http.Request(
196+
host="",
197+
port=0,
198+
method=typing.cast(str, request_json["method"]).encode("utf-8", "surrogateescape"),
199+
scheme=b"",
200+
authority=b"",
201+
path=b"",
202+
http_version=typing.cast(str, request_json["http_version"]
203+
if request_json["http_version"] is not None else "RI/UNSET").encode("utf-8", "surrogateescape"),
189204
headers=_headers_from_json(typing.cast(dict[str, list[str]], request_json["headers"])),
205+
content=None,
206+
trailers=_headers_from_json(typing.cast(dict[str, list[str]], request_json["trailers"]))
207+
if request_json["trailers"] is not None else None,
208+
timestamp_start=typing.cast(float, request_json["timestamp_start"]),
209+
timestamp_end=typing.cast(float, request_json["timestamp_end"]),
190210
)
191211

212+
# Use the URL setter to set the host, port, scheme, authority and path.
213+
request.url = typing.cast(str, request_json["url"])
214+
# Use the content setter to ensure that the content is encoded in accordance with the encoding headers.
215+
request.content = base64.b64decode(typing.cast(str, request_json["body"]))
216+
217+
return request
218+
192219

193220
def _response_to_json(response: http.Response) -> dict[str, object]:
194221
return {
222+
"http_version": response.http_version,
195223
"status_code": response.status_code,
196224
"reason": response.reason,
197225
"headers": _headers_to_json(response.headers),
198226
"body": base64.b64encode(response.get_content(strict=False)).decode("utf-8"),
227+
"trailers": _headers_to_json(response.trailers) if response.trailers else None,
228+
"timestamp_start": response.timestamp_start,
229+
"timestamp_end": response.timestamp_end,
199230
}
200231

201232

@@ -207,14 +238,22 @@ def _response_to_summary_json(response: http.Response) -> dict[str, object]:
207238

208239

209240
def _response_from_json(response_json: dict[str, object]) -> http.Response:
210-
response = http.Response.make(
241+
response = http.Response(
242+
http_version=typing.cast(str, response_json["http_version"]
243+
if response_json["http_version"] is not None else "RI/UNSET").encode("utf-8", "surrogateescape"),
211244
status_code=typing.cast(int, response_json["status_code"]),
212-
content=base64.b64decode(typing.cast(str, response_json["body"])),
245+
reason=typing.cast(str, response_json["reason"] or "").encode("ISO-8859-1"),
213246
headers=_headers_from_json(typing.cast(dict[str, list[str]], response_json["headers"])),
247+
content=None,
248+
trailers=_headers_from_json(typing.cast(dict[str, list[str]], response_json["trailers"]))
249+
if response_json["trailers"] is not None else None,
250+
timestamp_start=typing.cast(float, response_json["timestamp_start"]),
251+
timestamp_end=typing.cast(float, response_json["timestamp_end"]),
214252
)
215-
reason: str | None = response_json.get("reason")
216-
if reason is not None:
217-
response.reason = reason
253+
254+
# Use the content setter to ensure that the content is encoded in accordance with the encoding headers.
255+
response.content = base64.b64decode(typing.cast(str, response_json["body"]))
256+
218257
return response
219258

220259

0 commit comments

Comments
 (0)