Addons, HAR and replay¶
Ready-made filters ported from mitmproxy (map local and remote, modify bodies and headers, block lists, anti-cache, sticky cookies) with mitmproxy's option syntax and flow filter expressions, plus HAR recording and server and client replay. Use this page to change, record or replay traffic from the command line without writing code.
mitmproxy-style addons¶
org.microproxy.extras has ports of mitmproxy's request and response addons. Each is an
HttpFiltersSource, so they compose with each other, with your own filters and with scripts
through HttpFiltersChain, and each reads the same option syntax as mitmproxy:
[|flow-filter]|subject|replacement, where the first character is the separator (any character;
/ in mitmproxy's documentation) and the filter is optional.
bootstrap.withFiltersSource(HttpFiltersChain.of(
BlockList.of("|~d ads\\.example\\.com|404", "|~u /beacon|444"), // 444: close, no response
AntiCache.create(),
MapRemote.of("|https://api\\.example\\.com/|http://localhost:8000/"),
MapLocal.of("|example.com/static/|/srv/static"),
ModifyBody.of("|~s & ~t html|Example Domain|Local Domain"),
ModifyHeaders.of("|~q|User-Agent|MicroProxy", "|~s|Server|"), // empty value: remove
StickyCookie.of("~d example.com")));
java -jar microproxy.jar --mitm --map-remote '|https://api\.example\.com/|http://localhost:8000/' \
--map-local '|example.com/static/|/srv/static' --block-list '|~u /beacon|444' --anticache
| addon | command line | what it does |
|---|---|---|
MapLocal |
--map-local [|filter]|url-regex|file-or-dir |
answers from a file, or from a directory: the URL after the match (or the regex's first group) names the file; index.html for directories; a Content-Type from the extension; 404 when nothing matches; never outside the directory (.., absolute parts, links pointing out) |
MapRemote |
--map-remote [|filter]|url-regex|replacement |
replaces matches in the absolute URL (\1 or $1 for groups) and sends the request there, with Host to match; inside intercepted sessions too |
ModifyBody |
--modify-body [|filter]|regex|[@]replacement |
replaces matches in decoded request and response bodies (~q / ~s pick one side); the replacement is literal, @file reads it from a file |
ModifyHeaders |
--modify-headers [|filter]|name|[@]value |
removes the header, then adds the value (none if empty), on requests and on server responses |
BlockList |
--block-list |filter|status |
answers with an empty response of that status; 444 closes the connection without one (resets an HTTP/2 stream) |
AntiCache |
--anticache |
removes If-None-Match and If-Modified-Since, so servers send whole responses |
StickyCookie |
--stickycookie filter |
remembers every Set-Cookie (RFC 6265 Domain, Path, Max-Age/Expires, Secure) in one jar for all clients, and adds the cookies to requests the filter matches, keeping the client's own |
Filter expressions¶
Filter expressions are a compact subset of mitmproxy's (org.microproxy.extras.FlowFilter):
| expression | matches |
|---|---|
~u regex, or a bare regex |
the URL (absolute; https:// inside intercepted sessions) |
~d regex, ~m regex |
the host, the method |
~h regex, ~hq regex, ~hs regex |
a Name: value header line of the request or response, the request, the response |
~t regex, ~tq regex, ~ts regex |
the Content-Type of either, the request, the response |
~b regex, ~bq regex, ~bs regex |
the decoded body of either, the request, the response |
~c code, ~q, ~s, ~a, ~all |
the response status; no response yet; a response; an asset (CSS, JavaScript, images, fonts); everything |
!x, x & y, x | y, (x) |
not, and, or, grouping |
! binds tightest, then &, then |; expressions side by side are joined by "and" with the lowest
precedence, as in mitmproxy (a b | c is a & (b | c)). Regexes are java.util.regex, searched
anywhere and ignoring case; put one with spaces or ()~'" in quotes. A backslash in quotes only
escapes the quote and itself, so "\d+" keeps its \d (pyparsing would drop it).
Cost¶
The addons read heads only, so bodies keep the proxy's zero-copy relay. A body is buffered
only when a rule might need it, which the heads decide: ModifyBody buffers the messages a rule
might edit (and that it can decode; never event streams), and a filter that looks at a body
(~bq secret) buffers that body when the rest of the filter could still match. Body filters are
evaluated lazily, with three-valued logic: ~m GET & ~bq x never buffers a POST. Request bodies
over the limit (10 MiB by default, maxBodySize) get 413, as for any filter that buffers
requests; response bodies over it stream through unchanged.
The addon, HAR and replay options are command-line flags (and Java builders); properties files do not take them.
Order¶
The command line chains them as mitmproxy does: --save-har, --warc-dir and
--log-http first, then --block-list and --anticache, then a --script, then
--server-replay, --map-remote, --map-local, --modify-body, --modify-headers and
--stickycookie; the cache comes last. The first short-circuit wins, so a blocked request is never
replayed, and a script sees requests before they are mapped elsewhere.
Leaving an intercepted session¶
Requests inside a CONNECT that the proxy intercepts normally
go to the CONNECT target. When a filter gives one an absolute URI of its own, as MapRemote does
and as a script does by assigning req.uri = "https://other.example/...", the request goes to that
server instead: over TLS for https:// (with the MITM manager's trust), with Host set to the new
authority, and on a server connection kept and pooled under the new target. Changing only Host
or an origin-form path keeps the request on the session's server. The session's
ALPN mirroring still applies to the new server, and a plain HTTP session after
CONNECT and a reverse proxy are left the same way. Absolute https://
URIs in plain forward requests are fetched over TLS too.
HAR files¶
org.microproxy.extras.HarRecorder writes the exchanges it sees as an HTTP Archive (HAR 1.2), as
mitmproxy's --set hardump= and save.har do, for browser developer tools, HAR viewers and
replay:
java -jar microproxy.jar --mitm --save-har flows.har # written on exit
java -jar microproxy.jar --mitm --save-har flows.har --save-har-stream --save-har-filter '~d example.com'
HarRecorder har = HarRecorder.builder(Path.of("flows.har")).filter(FlowFilter.parse("!~a")).build();
MicroProxy.bootstrap().withFiltersSource(HttpFiltersChain.of(har, otherFilters)).start();
// ... har.close() writes the file; har.save(path) writes a snapshot at any time.
- Entries: the request as the client sent it (method, absolute URL, version, headers, cookies,
query string,
postDatawith formparams) and the response as the client received it (status, headers, cookies withexpires,redirectURL, decoded content as text, or base64 with"encoding": "base64"for binary bodies). AlsoserverIPAddress, the clientconnectionid, and_source(server,filter,cache,replayorproxy). Responses from filters, the cache and replays are recorded too;CONNECTtunnels are not. An exchange that ends without a response has status0and an_error. - Timings come from
FlowTimings:dns,connect(includingssl, as HAR says),send,waitandreceive, and-1for a phase that did not happen (no lookup or connect on a reused connection; inside an intercepted session the server handshake belongs to theCONNECT). - Bodies are captured as they stream past, up to
maxBodySizeeach (1 MiB by default,--save-har-max-body); a longer one is cut short and marked_truncated, and is not replayed. Capturing bodies turns off the zero-copy relay for recorded exchanges;content(false)(--save-har-max-body 0) records heads only and keeps it. - Memory: by default every entry, bodies included, stays in memory until the file is written on
exit (or
close()), as with mitmproxy'shardump: plan for up to twicemaxBodySizeper exchange (a third more for base64).stream(true)(--save-har-stream) appends each entry to the file as it completes and keeps nothing; the file is valid JSON once closed. A name ending in.zharis written zlib-compressed.
The JSON is written and read by a small package-private writer and parser shared with
HttpLogger and ActivityLogger; there is no JSON dependency.
Replaying traffic¶
Server replay¶
Server replay (ServerReplay, --server-replay path) answers requests from recorded
responses instead of contacting servers, as mitmproxy's server_replay. Recordings are HAR files
(from HarRecorder, browsers or mitmproxy, .har or .zhar), WARC files (from WarcRecorder:
--server-replay warcs/ takes the whole directory) or both:
java -jar microproxy.jar --mitm --server-replay flows.har --server-replay-ignore-params _,cb \
--server-replay-extra 404
ServerReplay replay = ServerReplay.builder().load(Path.of("warcs")).ignoreHost(true).reuse(true).build();
MicroProxy.bootstrap().withFiltersSource(replay).start();
- Matching ports mitmproxy's
_hash: scheme, method, path, request body, host, port and query parameters (in order) must be equal.--server-replay-ignore-content,-ignore-host,-ignore-port,-ignore-params a,band-ignore-payload-params a,b(form bodies) loosen it;--server-replay-use-headers a,badds headers. Request bodies are buffered to compare them (16 MiB at most). - Use: recordings answer once each, in recorded order, so a repeated request gets the recorded
sequence of responses;
--server-replay-reuselets them answer again and again. Requests without a recording are forwarded (the default), killed (--server-replay-extra kill, the connection is closed) or answered with an empty response (--server-replay-extra 404). - Refresh:
Date,Expires,Last-Modifiedand cookie expiry move forward by the time since the recording (--server-replay-no-refreshkeeps them). - Bodies: HAR content is decoded, so it is served without its
Content-Encoding; WARC bodies keep the coding they arrived with. The proxy setsContent-Length. - Source: replayed answers are
ResponseSource.REPLAYto trackers, access logs (REPLAY),HttpLogger(source=replay) andHarRecorder(_source). - All recordings are held in memory.
Client replay¶
Client replay (ClientReplay, --client-replay path) sends recorded requests again, as
mitmproxy's client_replay. They go to the proxy's own listener as ordinary client requests in
absolute form, so every filter, addon, script, cache and recorder applies, and the proxy fetches
https:// URLs over TLS. --client-replay-concurrency n (default 1) bounds the requests in
flight; the proxy stops when they are done, unless --keepserving is given. Recorded framing and
hop-by-hop headers are not sent, and a proxy that requires authentication answers them 407.
java -jar microproxy.jar --client-replay flows.har --save-har replayed.har # replay, record, exit
With scripts¶
The command-line addons and recorders compose with --script: the script runs
after --block-list and --anticache and before the other addons (as in mitmproxy), its
on_request can move a request elsewhere by assigning req.uri, and recorders and loggers placed
first see what the script changed. From Java, put a ScriptedProxy anywhere in an
HttpFiltersChain with the addons; a script that wants to keep a HAR of its own traffic can be
chained after a HarRecorder and the HAR written with har.save(path).