#!/usr/bin/env python3 """ Convert mitmproxy flow dump to HAR format Usage: mitmproxy-to-har.py """ import json import sys from datetime import datetime from mitmproxy import io from mitmproxy.http import HTTPFlow def flow_to_har_entry(flow: HTTPFlow) -> dict: """Convert single flow to HAR entry.""" if not flow.request or not flow.response: return None entry = { "startedDateTime": datetime.fromtimestamp(flow.request.timestamp_start).isoformat() + "Z", "time": int((flow.response.timestamp_end - flow.request.timestamp_start) * 1000) if flow.response.timestamp_end else 0, "request": { "method": flow.request.method, "url": flow.request.url, "httpVersion": flow.request.http_version, "headers": [{"name": k, "value": v} for k, v in flow.request.headers.items()], "queryString": [{"name": k, "value": v} for k, v in flow.request.query.items()], "cookies": [{"name": k, "value": v} for k, v in flow.request.cookies.items()], "headersSize": -1, "bodySize": len(flow.request.content) if flow.request.content else 0, }, "response": { "status": flow.response.status_code, "statusText": flow.response.reason, "httpVersion": flow.response.http_version, "headers": [{"name": k, "value": v} for k, v in flow.response.headers.items()], "cookies": [{"name": k, "value": v} for k, v in flow.response.cookies.items()], "content": { "size": len(flow.response.content) if flow.response.content else 0, "mimeType": flow.response.headers.get("Content-Type", "unknown").split(";")[0], "text": flow.response.get_text() if flow.response.content else "" }, "redirectURL": flow.response.headers.get("Location", ""), "headersSize": -1, "bodySize": len(flow.response.content) if flow.response.content else 0, }, "cache": {}, "timings": { "send": 0, "wait": int((flow.response.timestamp_start - flow.request.timestamp_end) * 1000) if flow.response.timestamp_start and flow.request.timestamp_end else 0, "receive": int((flow.response.timestamp_end - flow.response.timestamp_start) * 1000) if flow.response.timestamp_end and flow.response.timestamp_start else 0, } } # Add POST data if present if flow.request.method == "POST" and flow.request.content: entry["request"]["postData"] = { "mimeType": flow.request.headers.get("Content-Type", "application/octet-stream"), "text": flow.request.get_text() if flow.request.content else "" } return entry def convert_mitm_to_har(input_file: str, output_file: str): """Convert mitmproxy dump to HAR.""" har = { "log": { "version": "1.2", "creator": { "name": "mitmproxy-to-har", "version": "1.0" }, "entries": [] } } with open(input_file, 'rb') as f: reader = io.FlowReader(f) for flow in reader.read(): if isinstance(flow, HTTPFlow): entry = flow_to_har_entry(flow) if entry: har["log"]["entries"].append(entry) with open(output_file, 'w', encoding='utf-8') as f: json.dump(har, f, indent=2, ensure_ascii=False) print(f"Converted {len(har['log']['entries'])} flows to {output_file}") if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: mitmproxy-to-har.py ") sys.exit(1) convert_mitm_to_har(sys.argv[1], sys.argv[2])