Skip to content

Commit 48a663b

Browse files
committed
feat: preserve header order
1 parent a06a643 commit 48a663b

1 file changed

Lines changed: 36 additions & 10 deletions

File tree

src/mitmproxy_mcp/core/recorder.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@
1111
from .utils import get_safe_text
1212

1313

14+
def _parse_headers(raw: str) -> Dict[str, str]:
15+
"""Parse stored headers into a dict for backward compat.
16+
17+
Headers are stored as either:
18+
- list of [key, value] pairs (new format, preserves order)
19+
- dict (legacy format)
20+
Returns a dict in both cases. Duplicate keys are collapsed (last wins).
21+
"""
22+
parsed = json.loads(raw)
23+
if isinstance(parsed, list):
24+
return {k: v for k, v in parsed}
25+
return parsed
26+
27+
1428
class SimpleRequest:
1529
def __init__(self, method: str, url: str, headers: Dict[str, str], body: Optional[str]):
1630
self.method = method
@@ -93,9 +107,21 @@ def save_flow(self, flow: http.HTTPFlow):
93107
flow.request.url,
94108
flow.request.method,
95109
status_code,
96-
json.dumps(dict(flow.request.headers)),
110+
json.dumps(
111+
[
112+
[k.decode("latin-1"), v.decode("latin-1")]
113+
for k, v in flow.request.headers.fields
114+
],
115+
),
97116
req_body,
98-
json.dumps(dict(flow.response.headers)) if flow.response else None,
117+
json.dumps(
118+
[
119+
[k.decode("latin-1"), v.decode("latin-1")]
120+
for k, v in flow.response.headers.fields
121+
],
122+
)
123+
if flow.response
124+
else None,
99125
resp_body,
100126
flow.request.timestamp_start,
101127
size,
@@ -125,7 +151,7 @@ def get_summary(
125151
for row in rows:
126152
content_type = "unknown"
127153
if row["response_headers"]:
128-
headers = json.loads(row["response_headers"])
154+
headers = _parse_headers(row["response_headers"])
129155
content_type = headers.get(
130156
"content-type",
131157
headers.get("Content-Type", "unknown"),
@@ -153,8 +179,8 @@ def get_detail(self, flow_id: str) -> Optional[Dict[str, Any]]:
153179
if not row:
154180
return None
155181

156-
req_headers = json.loads(row["request_headers"])
157-
resp_headers = json.loads(row["response_headers"]) if row["response_headers"] else None
182+
req_headers = _parse_headers(row["request_headers"])
183+
resp_headers = _parse_headers(row["response_headers"]) if row["response_headers"] else None
158184

159185
simple_request = SimpleRequest(
160186
method=row["method"],
@@ -234,12 +260,12 @@ def get_all_for_analysis(self, limit: int = 1000) -> List[Dict[str, Any]]:
234260
"request": {
235261
"url": row["url"],
236262
"method": row["method"],
237-
"headers": json.loads(row["request_headers"]),
263+
"headers": _parse_headers(row["request_headers"]),
238264
"body": row["request_body"],
239265
},
240266
"response": {
241267
"status_code": row["status_code"],
242-
"headers": json.loads(row["response_headers"])
268+
"headers": _parse_headers(row["response_headers"])
243269
if row["response_headers"]
244270
else {},
245271
"body": row["response_body"],
@@ -269,12 +295,12 @@ def get_by_ids(self, flow_ids: List[str]) -> List[Dict[str, Any]]:
269295
"request": {
270296
"url": row["url"],
271297
"method": row["method"],
272-
"headers": json.loads(row["request_headers"]),
298+
"headers": _parse_headers(row["request_headers"]),
273299
"body": row["request_body"],
274300
},
275301
"response": {
276302
"status_code": row["status_code"],
277-
"headers": json.loads(row["response_headers"])
303+
"headers": _parse_headers(row["response_headers"])
278304
if row["response_headers"]
279305
else {},
280306
"body": row["response_body"],
@@ -315,7 +341,7 @@ def get_flow_object(self, flow_id: str) -> Optional[SimpleRequest]:
315341
if not row:
316342
return None
317343

318-
headers = json.loads(row["request_headers"])
344+
headers = _parse_headers(row["request_headers"])
319345
return SimpleRequest(
320346
method=row["method"],
321347
url=row["url"],

0 commit comments

Comments
 (0)