diff --git a/examples/addons/filter-flows.py b/examples/addons/filter-flows.py index a4e09ba3f..c5e7e6275 100644 --- a/examples/addons/filter-flows.py +++ b/examples/addons/filter-flows.py @@ -10,7 +10,8 @@ class Filter: self.filter: flowfilter.TFilter = None def configure(self, updated): - self.filter = flowfilter.parse(ctx.options.flowfilter) + if "flowfilter" in updated: + self.filter = flowfilter.parse(ctx.options.flowfilter) def load(self, l): l.add_option( diff --git a/mitmproxy/addons/blocklist.py b/mitmproxy/addons/blocklist.py index ceb6de91d..5a422cbbb 100644 --- a/mitmproxy/addons/blocklist.py +++ b/mitmproxy/addons/blocklist.py @@ -28,8 +28,6 @@ def parse_spec(option: str) -> BlockSpec: except ValueError: raise ValueError(f"Invalid HTTP status code: {status}") flow_filter = flowfilter.parse(flow_patt) - if not flow_filter: - raise ValueError(f"Invalid filter pattern: {flow_patt}") if not RESPONSES.get(status_code): raise ValueError(f"Invalid HTTP status code: {status}") diff --git a/mitmproxy/addons/dumper.py b/mitmproxy/addons/dumper.py index da0842b88..233bf8596 100644 --- a/mitmproxy/addons/dumper.py +++ b/mitmproxy/addons/dumper.py @@ -59,11 +59,10 @@ class Dumper: def configure(self, updated): if "dumper_filter" in updated: if ctx.options.dumper_filter: - self.filter = flowfilter.parse(ctx.options.dumper_filter) - if not self.filter: - raise exceptions.OptionsError( - "Invalid filter expression: %s" % ctx.options.dumper_filter - ) + try: + self.filter = flowfilter.parse(ctx.options.dumper_filter) + except ValueError as e: + raise exceptions.OptionsError(str(e)) from e else: self.filter = None diff --git a/mitmproxy/addons/intercept.py b/mitmproxy/addons/intercept.py index 5eebd6dc0..36f269bd4 100644 --- a/mitmproxy/addons/intercept.py +++ b/mitmproxy/addons/intercept.py @@ -21,9 +21,10 @@ class Intercept: def configure(self, updated): if "intercept" in updated: if ctx.options.intercept: - self.filt = flowfilter.parse(ctx.options.intercept) - if not self.filt: - raise exceptions.OptionsError(f"Invalid interception filter: {ctx.options.intercept}") + try: + self.filt = flowfilter.parse(ctx.options.intercept) + except ValueError as e: + raise exceptions.OptionsError(str(e)) from e ctx.options.intercept_active = True else: self.filt = None diff --git a/mitmproxy/addons/readfile.py b/mitmproxy/addons/readfile.py index 607b41b40..c75bc8f7d 100644 --- a/mitmproxy/addons/readfile.py +++ b/mitmproxy/addons/readfile.py @@ -30,14 +30,13 @@ class ReadFile: def configure(self, updated): if "readfile_filter" in updated: - filt = None if ctx.options.readfile_filter: - filt = flowfilter.parse(ctx.options.readfile_filter) - if not filt: - raise exceptions.OptionsError( - "Invalid readfile filter: %s" % ctx.options.readfile_filter - ) - self.filter = filt + try: + self.filter = flowfilter.parse(ctx.options.readfile_filter) + except ValueError as e: + raise exceptions.OptionsError(str(e)) from e + else: + self.filter = None async def load_flows(self, fo: typing.IO[bytes]) -> int: cnt = 0 diff --git a/mitmproxy/addons/save.py b/mitmproxy/addons/save.py index 9297e7399..3b3766dc6 100644 --- a/mitmproxy/addons/save.py +++ b/mitmproxy/addons/save.py @@ -48,11 +48,10 @@ class Save: # We're already streaming - stop the previous stream and restart if "save_stream_filter" in updated: if ctx.options.save_stream_filter: - self.filt = flowfilter.parse(ctx.options.save_stream_filter) - if not self.filt: - raise exceptions.OptionsError( - "Invalid filter specification: %s" % ctx.options.save_stream_filter - ) + try: + self.filt = flowfilter.parse(ctx.options.save_stream_filter) + except ValueError as e: + raise exceptions.OptionsError(str(e)) from e else: self.filt = None if "save_stream_file" in updated or "save_stream_filter" in updated: diff --git a/mitmproxy/addons/stickyauth.py b/mitmproxy/addons/stickyauth.py index ab0599eef..43098b2d7 100644 --- a/mitmproxy/addons/stickyauth.py +++ b/mitmproxy/addons/stickyauth.py @@ -19,12 +19,10 @@ class StickyAuth: def configure(self, updated): if "stickyauth" in updated: if ctx.options.stickyauth: - flt = flowfilter.parse(ctx.options.stickyauth) - if not flt: - raise exceptions.OptionsError( - "stickyauth: invalid filter expression: %s" % ctx.options.stickyauth - ) - self.flt = flt + try: + self.flt = flowfilter.parse(ctx.options.stickyauth) + except ValueError as e: + raise exceptions.OptionsError(str(e)) from e else: self.flt = None diff --git a/mitmproxy/addons/stickycookie.py b/mitmproxy/addons/stickycookie.py index 1651c1f66..3b67bb82c 100644 --- a/mitmproxy/addons/stickycookie.py +++ b/mitmproxy/addons/stickycookie.py @@ -43,12 +43,10 @@ class StickyCookie: def configure(self, updated): if "stickycookie" in updated: if ctx.options.stickycookie: - flt = flowfilter.parse(ctx.options.stickycookie) - if not flt: - raise exceptions.OptionsError( - "stickycookie: invalid filter expression: %s" % ctx.options.stickycookie - ) - self.flt = flt + try: + self.flt = flowfilter.parse(ctx.options.stickycookie) + except ValueError as e: + raise exceptions.OptionsError(str(e)) from e else: self.flt = None diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py index 13c5baf17..b8d738d33 100644 --- a/mitmproxy/addons/view.py +++ b/mitmproxy/addons/view.py @@ -115,8 +115,6 @@ class OrderKeySize(_OrderKey): raise NotImplementedError() -matchall = flowfilter.parse("~http | ~tcp") - orders = [ ("t", "time"), ("m", "method"), @@ -129,7 +127,7 @@ class View(collections.abc.Sequence): def __init__(self): super().__init__() self._store = collections.OrderedDict() - self.filter = matchall + self.filter = flowfilter.match_all # Should we show only marked flows? self.show_marked = False @@ -326,15 +324,14 @@ class View(collections.abc.Sequence): """ filt = None if filter_expr: - filt = flowfilter.parse(filter_expr) - if not filt: - raise exceptions.CommandError( - "Invalid interception filter: %s" % filter_expr - ) + try: + filt = flowfilter.parse(filter_expr) + except ValueError as e: + raise exceptions.CommandError(str(e)) from e self.set_filter(filt) def set_filter(self, flt: typing.Optional[flowfilter.TFilter]): - self.filter = flt or matchall + self.filter = flt or flowfilter.match_all self._refilter() # View Updates @@ -454,9 +451,10 @@ class View(collections.abc.Sequence): ids = flow_spec[1:].split(",") return [i for i in self._store.values() if i.id in ids] else: - filt = flowfilter.parse(flow_spec) - if not filt: - raise exceptions.CommandError("Invalid flow filter: %s" % flow_spec) + try: + filt = flowfilter.parse(flow_spec) + except ValueError as e: + raise exceptions.CommandError(str(e)) from e return [i for i in self._store.values() if filt(i)] @command.command("view.flows.create") @@ -547,11 +545,10 @@ class View(collections.abc.Sequence): if "view_filter" in updated: filt = None if ctx.options.view_filter: - filt = flowfilter.parse(ctx.options.view_filter) - if not filt: - raise exceptions.OptionsError( - "Invalid interception filter: %s" % ctx.options.view_filter - ) + try: + filt = flowfilter.parse(ctx.options.view_filter) + except ValueError as e: + raise exceptions.OptionsError(str(e)) from e self.set_filter(filt) if "view_order" in updated: if ctx.options.view_order not in self.orders: diff --git a/mitmproxy/flowfilter.py b/mitmproxy/flowfilter.py index fea8e9e42..29ba0af84 100644 --- a/mitmproxy/flowfilter.py +++ b/mitmproxy/flowfilter.py @@ -35,7 +35,7 @@ import functools import re import sys -from typing import Callable, ClassVar, Optional, Sequence, Type +from typing import ClassVar, Sequence, Type, Protocol, Union import pyparsing as pp from mitmproxy import flow, http, tcp @@ -135,6 +135,14 @@ class FResp(_Action): return bool(f.response) +class FAll(_Action): + code = "all" + help = "Match all flows" + + def __call__(self, f: flow.Flow): + return True + + class _Rex(_Action): flags = 0 is_binary = True @@ -504,6 +512,7 @@ filter_unary: Sequence[Type[_Action]] = [ FResp, FTCP, FWebSocket, + FAll, ] filter_rex: Sequence[Type[_Rex]] = [ FBod, @@ -583,21 +592,31 @@ def _make(): bnf = _make() -TFilter = Callable[[flow.Flow], bool] -def parse(s: str) -> Optional[TFilter]: +class TFilter(Protocol): + pattern: str + + def __call__(self, f: flow.Flow) -> bool: + ... # pragma: no cover + + +def parse(s: str) -> TFilter: + """ + Parse a filter expression and return the compiled filter function. + If the filter syntax is invalid, `ValueError` is raised. + """ + if not s: + raise ValueError("Empty filter expression") try: flt = bnf.parseString(s, parseAll=True)[0] flt.pattern = s return flt - except pp.ParseException: - return None - except ValueError: - return None + except (pp.ParseException, ValueError) as e: + raise ValueError(f"Invalid filter expression: {s!r}") from e -def match(flt, flow): +def match(flt: Union[str, TFilter], flow: flow.Flow) -> bool: """ Matches a flow against a compiled filter expression. Returns True if matched, False if not. @@ -607,13 +626,15 @@ def match(flt, flow): """ if isinstance(flt, str): flt = parse(flt) - if not flt: - raise ValueError("Invalid filter expression.") if flt: return flt(flow) return True +match_all: TFilter = parse("~all") +"""A filter function that matches all flows""" + + help = [] for a in filter_unary: help.append( diff --git a/mitmproxy/tools/web/app.py b/mitmproxy/tools/web/app.py index 9deba965e..8746c7e8e 100644 --- a/mitmproxy/tools/web/app.py +++ b/mitmproxy/tools/web/app.py @@ -58,6 +58,7 @@ def flow_to_json(flow: mitmproxy.flow.Flow) -> dict: "type": flow.type, "modified": flow.modified(), "marked": emoji.get(flow.marked, "🔴") if flow.marked else "", + "comment": flow.comment, } if flow.client_conn: diff --git a/mitmproxy/utils/spec.py b/mitmproxy/utils/spec.py index c38fe92e8..1298685b9 100644 --- a/mitmproxy/utils/spec.py +++ b/mitmproxy/utils/spec.py @@ -2,10 +2,6 @@ import typing from mitmproxy import flowfilter -def _match_all(flow) -> bool: - return True - - def parse_spec(option: str) -> typing.Tuple[flowfilter.TFilter, str, str]: """ Parse strings in the following format: @@ -17,12 +13,10 @@ def parse_spec(option: str) -> typing.Tuple[flowfilter.TFilter, str, str]: parts = rem.split(sep, 2) if len(parts) == 2: subject, replacement = parts - return _match_all, subject, replacement + return flowfilter.match_all, subject, replacement elif len(parts) == 3: patt, subject, replacement = parts flow_filter = flowfilter.parse(patt) - if not flow_filter: - raise ValueError(f"Invalid filter pattern: {patt}") return flow_filter, subject, replacement else: raise ValueError("Invalid number of parameters (2 or 3 are expected)") diff --git a/test/mitmproxy/addons/test_readfile.py b/test/mitmproxy/addons/test_readfile.py index 8a9c7b8e1..42a1a1768 100644 --- a/test/mitmproxy/addons/test_readfile.py +++ b/test/mitmproxy/addons/test_readfile.py @@ -43,8 +43,9 @@ class TestReadFile: rf = readfile.ReadFile() with taddons.context(rf) as tctx: tctx.configure(rf, readfile_filter="~q") - with pytest.raises(Exception, match="Invalid readfile filter"): + with pytest.raises(Exception, match="Invalid filter expression"): tctx.configure(rf, readfile_filter="~~") + tctx.configure(rf, readfile_filter="") @pytest.mark.asyncio async def test_read(self, tmpdir, data, corrupt_data): diff --git a/test/mitmproxy/addons/test_stickycookie.py b/test/mitmproxy/addons/test_stickycookie.py index 4493e9cc2..25fc085ab 100644 --- a/test/mitmproxy/addons/test_stickycookie.py +++ b/test/mitmproxy/addons/test_stickycookie.py @@ -16,7 +16,7 @@ class TestStickyCookie: def test_config(self): sc = stickycookie.StickyCookie() with taddons.context(sc) as tctx: - with pytest.raises(Exception, match="invalid filter"): + with pytest.raises(Exception, match="Invalid filter expression"): tctx.configure(sc, stickycookie="~b") tctx.configure(sc, stickycookie="foo") diff --git a/test/mitmproxy/addons/test_view.py b/test/mitmproxy/addons/test_view.py index 61ddfdd30..0032a7823 100644 --- a/test/mitmproxy/addons/test_view.py +++ b/test/mitmproxy/addons/test_view.py @@ -268,7 +268,7 @@ def test_resolve(): assert m(tctx.command(v.resolve, "@unmarked")) == ["PUT", "GET", "PUT"] assert m(tctx.command(v.resolve, "@all")) == ["GET", "PUT", "GET", "PUT"] - with pytest.raises(exceptions.CommandError, match="Invalid flow filter"): + with pytest.raises(exceptions.CommandError, match="Invalid filter expression"): tctx.command(v.resolve, "~") @@ -608,7 +608,7 @@ def test_configure(): v = view.View() with taddons.context(v) as tctx: tctx.configure(v, view_filter="~q") - with pytest.raises(Exception, match="Invalid interception filter"): + with pytest.raises(Exception, match="Invalid filter expression"): tctx.configure(v, view_filter="~~") tctx.configure(v, view_order="method") diff --git a/test/mitmproxy/test_flowfilter.py b/test/mitmproxy/test_flowfilter.py index 9105b4243..718332520 100644 --- a/test/mitmproxy/test_flowfilter.py +++ b/test/mitmproxy/test_flowfilter.py @@ -13,10 +13,14 @@ class TestParsing: assert c.getvalue() def test_parse_err(self): - assert flowfilter.parse("~h [") is None + with pytest.raises(ValueError, match="Empty filter"): + flowfilter.parse("") + with pytest.raises(ValueError, match="Invalid filter"): + flowfilter.parse("~b") + with pytest.raises(ValueError, match="Invalid filter"): + flowfilter.parse("~h [") def test_simple(self): - assert not flowfilter.parse("~b") assert flowfilter.parse("~q") assert flowfilter.parse("~c 10") assert flowfilter.parse("~m foobar") @@ -568,6 +572,8 @@ class TestMatchingDummyFlow: f = self.flow() f.server_conn = tflow.tserver_conn() + assert self.q("~all", f) + assert not self.q("~a", f) assert not self.q("~b whatever", f) diff --git a/test/mitmproxy/tools/console/test_integration.py b/test/mitmproxy/tools/console/test_integration.py index 4bc1c0000..ed80bad2f 100644 --- a/test/mitmproxy/tools/console/test_integration.py +++ b/test/mitmproxy/tools/console/test_integration.py @@ -39,15 +39,18 @@ def console(monkeypatch): return m +@pytest.mark.asyncio def test_integration(tdata, console): console.type(f":view.flows.load {tdata.path('mitmproxy/data/dumpfile-7.mitm')}") console.type("") console.type("") # view second flow +@pytest.mark.asyncio def test_options_home_end(console): console.type("O") +@pytest.mark.asyncio def test_keybindings_home_end(console): - console.type("K") \ No newline at end of file + console.type("K") diff --git a/test/mitmproxy/tools/web/test_app.py b/test/mitmproxy/tools/web/test_app.py index 99cbb61bd..ae4ee0414 100644 --- a/test/mitmproxy/tools/web/test_app.py +++ b/test/mitmproxy/tools/web/test_app.py @@ -48,6 +48,7 @@ def test_generate_tflow_js(tdata): ] tf_http.request.trailers = Headers(trailer="qvalue") tf_http.response.trailers = Headers(trailer="qvalue") + tf_http.comment = "I'm a comment!" tf_tcp = tflow.ttcpflow(err=True) tf_tcp.id = "2ea7012b-21b5-4f8f-98cd-d49819954001" diff --git a/test/mitmproxy/utils/test_spec.py b/test/mitmproxy/utils/test_spec.py index 63a063563..6cefcacc7 100644 --- a/test/mitmproxy/utils/test_spec.py +++ b/test/mitmproxy/utils/test_spec.py @@ -16,5 +16,5 @@ def test_parse_spec(): with pytest.raises(ValueError, match="Invalid number of parameters"): parse_spec("/") - with pytest.raises(ValueError, match="Invalid filter pattern"): + with pytest.raises(ValueError, match="Invalid filter expression"): parse_spec("/~b/one/two") diff --git a/web/src/css/flowtable.less b/web/src/css/flowtable.less index e258814a4..c251ff888 100644 --- a/web/src/css/flowtable.less +++ b/web/src/css/flowtable.less @@ -64,6 +64,9 @@ &.selected { background-color: #e0ebf5 !important; } + &.selected.highlighted { + background-color: #7bbefc !important; + } &.highlighted { background-color: #ffeb99; diff --git a/web/src/js/__tests__/ducks/_tflow.ts b/web/src/js/__tests__/ducks/_tflow.ts index f2685e1cc..072d78e4a 100644 --- a/web/src/js/__tests__/ducks/_tflow.ts +++ b/web/src/js/__tests__/ducks/_tflow.ts @@ -22,6 +22,7 @@ export function THTTPFlow(): Required { "tls_established": true, "tls_version": "TLSv1.2" }, + "comment": "I'm a comment!", "error": { "msg": "error", "timestamp": 946681207.0 @@ -180,6 +181,7 @@ export function TTCPFlow(): Required { "tls_established": true, "tls_version": "TLSv1.2" }, + "comment": "", "error": { "msg": "error", "timestamp": 946681207.0 diff --git a/web/src/js/__tests__/urlStateSpec.tsx b/web/src/js/__tests__/urlStateSpec.tsx index 9f48da761..31a0d8c53 100644 --- a/web/src/js/__tests__/urlStateSpec.tsx +++ b/web/src/js/__tests__/urlStateSpec.tsx @@ -83,7 +83,7 @@ describe('updateUrlFromStore', () => { }, store = mockStore(state) updateUrlFromStore(store) - expect(history.replaceState).toBeCalledWith(undefined, '', '/#/flows/123/request?s=~u foo') + expect(history.replaceState).toBeCalledWith(undefined, '', '/#/flows/123/request?s=~u%20foo') }) }) diff --git a/web/src/js/filt/filt.js b/web/src/js/filt/filt.js index 4131311bc..3b7699362 100644 --- a/web/src/js/filt/filt.js +++ b/web/src/js/filt/filt.js @@ -60,27 +60,27 @@ export default (function() { peg$c20 = ")", peg$c21 = { type: "literal", value: ")", description: "\")\"" }, peg$c22 = function(expr) { return binding(expr); }, - peg$c23 = "true", - peg$c24 = { type: "literal", value: "true", description: "\"true\"" }, - peg$c25 = function() { return trueFilter; }, - peg$c26 = "false", - peg$c27 = { type: "literal", value: "false", description: "\"false\"" }, - peg$c28 = function() { return falseFilter; }, - peg$c29 = "~a", - peg$c30 = { type: "literal", value: "~a", description: "\"~a\"" }, - peg$c31 = function() { return assetFilter; }, - peg$c32 = "~b", - peg$c33 = { type: "literal", value: "~b", description: "\"~b\"" }, - peg$c34 = function(s) { return body(s); }, - peg$c35 = "~bq", - peg$c36 = { type: "literal", value: "~bq", description: "\"~bq\"" }, - peg$c37 = function(s) { return requestBody(s); }, - peg$c38 = "~bs", - peg$c39 = { type: "literal", value: "~bs", description: "\"~bs\"" }, - peg$c40 = function(s) { return responseBody(s); }, - peg$c41 = "~c", - peg$c42 = { type: "literal", value: "~c", description: "\"~c\"" }, - peg$c43 = function(s) { return responseCode(s); }, + peg$c23 = "~all", + peg$c24 = { type: "literal", value: "~all", description: "\"~all\"" }, + peg$c25 = function() { return allFilter; }, + peg$c26 = "~a", + peg$c27 = { type: "literal", value: "~a", description: "\"~a\"" }, + peg$c28 = function() { return assetFilter; }, + peg$c29 = "~b", + peg$c30 = { type: "literal", value: "~b", description: "\"~b\"" }, + peg$c31 = function(s) { return body(s); }, + peg$c32 = "~bq", + peg$c33 = { type: "literal", value: "~bq", description: "\"~bq\"" }, + peg$c34 = function(s) { return requestBody(s); }, + peg$c35 = "~bs", + peg$c36 = { type: "literal", value: "~bs", description: "\"~bs\"" }, + peg$c37 = function(s) { return responseBody(s); }, + peg$c38 = "~c", + peg$c39 = { type: "literal", value: "~c", description: "\"~c\"" }, + peg$c40 = function(s) { return responseCode(s); }, + peg$c41 = "~comment", + peg$c42 = { type: "literal", value: "~comment", description: "\"~comment\"" }, + peg$c43 = function(s) { return comment(s); }, peg$c44 = "~d", peg$c45 = { type: "literal", value: "~d", description: "\"~d\"" }, peg$c46 = function(s) { return domain(s); }, @@ -102,70 +102,82 @@ export default (function() { peg$c62 = "~http", peg$c63 = { type: "literal", value: "~http", description: "\"~http\"" }, peg$c64 = function() { return httpFilter; }, - peg$c65 = "~m", - peg$c66 = { type: "literal", value: "~m", description: "\"~m\"" }, - peg$c67 = function(s) { return method(s); }, - peg$c68 = "~marked", - peg$c69 = { type: "literal", value: "~marked", description: "\"~marked\"" }, - peg$c70 = function() { return markedFilter; }, - peg$c71 = "~q", - peg$c72 = { type: "literal", value: "~q", description: "\"~q\"" }, - peg$c73 = function() { return noResponseFilter; }, - peg$c74 = "~src", - peg$c75 = { type: "literal", value: "~src", description: "\"~src\"" }, - peg$c76 = function(s) { return source(s); }, - peg$c77 = "~s", - peg$c78 = { type: "literal", value: "~s", description: "\"~s\"" }, - peg$c79 = function() { return responseFilter; }, - peg$c80 = "~t", - peg$c81 = { type: "literal", value: "~t", description: "\"~t\"" }, - peg$c82 = function(s) { return contentType(s); }, - peg$c83 = "~tcp", - peg$c84 = { type: "literal", value: "~tcp", description: "\"~tcp\"" }, - peg$c85 = function() { return tcpFilter; }, - peg$c86 = "~tq", - peg$c87 = { type: "literal", value: "~tq", description: "\"~tq\"" }, - peg$c88 = function(s) { return requestContentType(s); }, - peg$c89 = "~ts", - peg$c90 = { type: "literal", value: "~ts", description: "\"~ts\"" }, - peg$c91 = function(s) { return responseContentType(s); }, - peg$c92 = "~u", - peg$c93 = { type: "literal", value: "~u", description: "\"~u\"" }, - peg$c94 = function(s) { return url(s); }, - peg$c95 = "~websocket", - peg$c96 = { type: "literal", value: "~websocket", description: "\"~websocket\"" }, - peg$c97 = function() { return websocketFilter; }, - peg$c98 = { type: "other", description: "integer" }, - peg$c99 = /^['"]/, - peg$c100 = { type: "class", value: "['\"]", description: "['\"]" }, - peg$c101 = /^[0-9]/, - peg$c102 = { type: "class", value: "[0-9]", description: "[0-9]" }, - peg$c103 = function(digits) { return parseInt(digits.join(""), 10); }, - peg$c104 = { type: "other", description: "string" }, - peg$c105 = "\"", - peg$c106 = { type: "literal", value: "\"", description: "\"\\\"\"" }, - peg$c107 = function(chars) { return chars.join(""); }, - peg$c108 = "'", - peg$c109 = { type: "literal", value: "'", description: "\"'\"" }, - peg$c110 = /^["\\]/, - peg$c111 = { type: "class", value: "[\"\\\\]", description: "[\"\\\\]" }, - peg$c112 = { type: "any", description: "any character" }, - peg$c113 = function(char) { return char; }, - peg$c114 = "\\", - peg$c115 = { type: "literal", value: "\\", description: "\"\\\\\"" }, - peg$c116 = /^['\\]/, - peg$c117 = { type: "class", value: "['\\\\]", description: "['\\\\]" }, - peg$c118 = /^['"\\]/, - peg$c119 = { type: "class", value: "['\"\\\\]", description: "['\"\\\\]" }, - peg$c120 = "n", - peg$c121 = { type: "literal", value: "n", description: "\"n\"" }, - peg$c122 = function() { return "\n"; }, - peg$c123 = "r", - peg$c124 = { type: "literal", value: "r", description: "\"r\"" }, - peg$c125 = function() { return "\r"; }, - peg$c126 = "t", - peg$c127 = { type: "literal", value: "t", description: "\"t\"" }, - peg$c128 = function() { return "\t"; }, + peg$c65 = "~marked", + peg$c66 = { type: "literal", value: "~marked", description: "\"~marked\"" }, + peg$c67 = function() { return markedFilter; }, + peg$c68 = "~marker", + peg$c69 = { type: "literal", value: "~marker", description: "\"~marker\"" }, + peg$c70 = function(s) { return marker(s); }, + peg$c71 = "~m", + peg$c72 = { type: "literal", value: "~m", description: "\"~m\"" }, + peg$c73 = function(s) { return method(s); }, + peg$c74 = "~q", + peg$c75 = { type: "literal", value: "~q", description: "\"~q\"" }, + peg$c76 = function() { return noResponseFilter; }, + peg$c77 = "~replayq", + peg$c78 = { type: "literal", value: "~replayq", description: "\"~replayq\"" }, + peg$c79 = function() { return clientReplayFilter; }, + peg$c80 = "~replays", + peg$c81 = { type: "literal", value: "~replays", description: "\"~replays\"" }, + peg$c82 = function() { return serverReplayFilter; }, + peg$c83 = "~replay", + peg$c84 = { type: "literal", value: "~replay", description: "\"~replay\"" }, + peg$c85 = function() { return replayFilter; }, + peg$c86 = "~src", + peg$c87 = { type: "literal", value: "~src", description: "\"~src\"" }, + peg$c88 = function(s) { return source(s); }, + peg$c89 = "~s", + peg$c90 = { type: "literal", value: "~s", description: "\"~s\"" }, + peg$c91 = function() { return responseFilter; }, + peg$c92 = "~tcp", + peg$c93 = { type: "literal", value: "~tcp", description: "\"~tcp\"" }, + peg$c94 = function() { return tcpFilter; }, + peg$c95 = "~tq", + peg$c96 = { type: "literal", value: "~tq", description: "\"~tq\"" }, + peg$c97 = function(s) { return requestContentType(s); }, + peg$c98 = "~ts", + peg$c99 = { type: "literal", value: "~ts", description: "\"~ts\"" }, + peg$c100 = function(s) { return responseContentType(s); }, + peg$c101 = "~t", + peg$c102 = { type: "literal", value: "~t", description: "\"~t\"" }, + peg$c103 = function(s) { return contentType(s); }, + peg$c104 = "~u", + peg$c105 = { type: "literal", value: "~u", description: "\"~u\"" }, + peg$c106 = function(s) { return url(s); }, + peg$c107 = "~websocket", + peg$c108 = { type: "literal", value: "~websocket", description: "\"~websocket\"" }, + peg$c109 = function() { return websocketFilter; }, + peg$c110 = { type: "other", description: "integer" }, + peg$c111 = /^['"]/, + peg$c112 = { type: "class", value: "['\"]", description: "['\"]" }, + peg$c113 = /^[0-9]/, + peg$c114 = { type: "class", value: "[0-9]", description: "[0-9]" }, + peg$c115 = function(digits) { return parseInt(digits.join(""), 10); }, + peg$c116 = { type: "other", description: "string" }, + peg$c117 = "\"", + peg$c118 = { type: "literal", value: "\"", description: "\"\\\"\"" }, + peg$c119 = function(chars) { return chars.join(""); }, + peg$c120 = "'", + peg$c121 = { type: "literal", value: "'", description: "\"'\"" }, + peg$c122 = /^["\\]/, + peg$c123 = { type: "class", value: "[\"\\\\]", description: "[\"\\\\]" }, + peg$c124 = { type: "any", description: "any character" }, + peg$c125 = function(char) { return char; }, + peg$c126 = "\\", + peg$c127 = { type: "literal", value: "\\", description: "\"\\\\\"" }, + peg$c128 = /^['\\]/, + peg$c129 = { type: "class", value: "['\\\\]", description: "['\\\\]" }, + peg$c130 = /^['"\\]/, + peg$c131 = { type: "class", value: "['\"\\\\]", description: "['\"\\\\]" }, + peg$c132 = "n", + peg$c133 = { type: "literal", value: "n", description: "\"n\"" }, + peg$c134 = function() { return "\n"; }, + peg$c135 = "r", + peg$c136 = { type: "literal", value: "r", description: "\"r\"" }, + peg$c137 = function() { return "\r"; }, + peg$c138 = "t", + peg$c139 = { type: "literal", value: "t", description: "\"t\"" }, + peg$c140 = function() { return "\t"; }, peg$currPos = 0, peg$savedPos = 0, @@ -691,9 +703,9 @@ export default (function() { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 5) === peg$c26) { + if (input.substr(peg$currPos, 2) === peg$c26) { s1 = peg$c26; - peg$currPos += 5; + peg$currPos += 2; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c27); } @@ -713,15 +725,39 @@ export default (function() { if (peg$silentFails === 0) { peg$fail(peg$c30); } } if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c31(); + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c31(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; } - s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c32) { + if (input.substr(peg$currPos, 3) === peg$c32) { s1 = peg$c32; - peg$currPos += 2; + peg$currPos += 3; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c33); } @@ -795,9 +831,9 @@ export default (function() { } if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 3) === peg$c38) { + if (input.substr(peg$currPos, 2) === peg$c38) { s1 = peg$c38; - peg$currPos += 3; + peg$currPos += 2; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c39); } @@ -814,7 +850,7 @@ export default (function() { s2 = peg$FAILED; } if (s2 !== peg$FAILED) { - s3 = peg$parseStringLiteral(); + s3 = peg$parseIntegerLiteral(); if (s3 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c40(s3); @@ -833,9 +869,9 @@ export default (function() { } if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c41) { + if (input.substr(peg$currPos, 8) === peg$c41) { s1 = peg$c41; - peg$currPos += 2; + peg$currPos += 8; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c42); } @@ -852,7 +888,7 @@ export default (function() { s2 = peg$FAILED; } if (s2 !== peg$FAILED) { - s3 = peg$parseIntegerLiteral(); + s3 = peg$parseStringLiteral(); if (s3 !== peg$FAILED) { peg$savedPos = s0; s1 = peg$c43(s3); @@ -1089,42 +1125,18 @@ export default (function() { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c65) { + if (input.substr(peg$currPos, 7) === peg$c65) { s1 = peg$c65; - peg$currPos += 2; + peg$currPos += 7; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c66); } } if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parsews(); - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - s3 = peg$parseStringLiteral(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c67(s3); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; + peg$savedPos = s0; + s1 = peg$c67(); } + s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.substr(peg$currPos, 7) === peg$c68) { @@ -1135,10 +1147,34 @@ export default (function() { if (peg$silentFails === 0) { peg$fail(peg$c69); } } if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c70(); + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c70(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; } - s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.substr(peg$currPos, 2) === peg$c71) { @@ -1149,40 +1185,22 @@ export default (function() { if (peg$silentFails === 0) { peg$fail(peg$c72); } } if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c73(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c74) { - s1 = peg$c74; - peg$currPos += 4; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c75); } - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parsews(); - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); - } - } else { - s2 = peg$FAILED; + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); } - if (s2 !== peg$FAILED) { - s3 = peg$parseStringLiteral(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c76(s3); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c73(s3); + s0 = s1; } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1191,11 +1209,29 @@ export default (function() { peg$currPos = s0; s0 = peg$FAILED; } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c74) { + s1 = peg$c74; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c75); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c76(); + } + s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c77) { + if (input.substr(peg$currPos, 8) === peg$c77) { s1 = peg$c77; - peg$currPos += 2; + peg$currPos += 8; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c78); } @@ -1207,47 +1243,23 @@ export default (function() { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c80) { + if (input.substr(peg$currPos, 8) === peg$c80) { s1 = peg$c80; - peg$currPos += 2; + peg$currPos += 8; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c81); } } if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parsews(); - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - s3 = peg$parseStringLiteral(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c82(s3); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; + peg$savedPos = s0; + s1 = peg$c82(); } + s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 4) === peg$c83) { + if (input.substr(peg$currPos, 7) === peg$c83) { s1 = peg$c83; - peg$currPos += 4; + peg$currPos += 7; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c84); } @@ -1259,9 +1271,9 @@ export default (function() { s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 3) === peg$c86) { + if (input.substr(peg$currPos, 4) === peg$c86) { s1 = peg$c86; - peg$currPos += 3; + peg$currPos += 4; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c87); } @@ -1297,68 +1309,62 @@ export default (function() { } if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 3) === peg$c89) { + if (input.substr(peg$currPos, 2) === peg$c89) { s1 = peg$c89; - peg$currPos += 3; + peg$currPos += 2; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c90); } } if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parsews(); - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - s3 = peg$parseStringLiteral(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c91(s3); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; + peg$savedPos = s0; + s1 = peg$c91(); } + s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c92) { + if (input.substr(peg$currPos, 4) === peg$c92) { s1 = peg$c92; - peg$currPos += 2; + peg$currPos += 4; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$c93); } } if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parsews(); - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); - } + peg$savedPos = s0; + s1 = peg$c94(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 3) === peg$c95) { + s1 = peg$c95; + peg$currPos += 3; } else { - s2 = peg$FAILED; + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c96); } } - if (s2 !== peg$FAILED) { - s3 = peg$parseStringLiteral(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c94(s3); - s0 = s1; + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c97(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } } else { peg$currPos = s0; s0 = peg$FAILED; @@ -1367,32 +1373,146 @@ export default (function() { peg$currPos = s0; s0 = peg$FAILED; } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.substr(peg$currPos, 10) === peg$c95) { - s1 = peg$c95; - peg$currPos += 10; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c96); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c97(); - } - s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - s1 = peg$parseStringLiteral(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c94(s1); + if (input.substr(peg$currPos, 3) === peg$c98) { + s1 = peg$c98; + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c99); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c100(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c101) { + s1 = peg$c101; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c102); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c103(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c104) { + s1 = peg$c104; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c105); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseStringLiteral(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c106(s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 10) === peg$c107) { + s1 = peg$c107; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c108); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c109(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseStringLiteral(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c106(s1); + } + s0 = s1; + } + } + } } - s0 = s1; } } } @@ -1427,53 +1547,53 @@ export default (function() { peg$silentFails++; s0 = peg$currPos; - if (peg$c99.test(input.charAt(peg$currPos))) { + if (peg$c111.test(input.charAt(peg$currPos))) { s1 = input.charAt(peg$currPos); peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c100); } + if (peg$silentFails === 0) { peg$fail(peg$c112); } } if (s1 === peg$FAILED) { s1 = null; } if (s1 !== peg$FAILED) { s2 = []; - if (peg$c101.test(input.charAt(peg$currPos))) { + if (peg$c113.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c102); } + if (peg$silentFails === 0) { peg$fail(peg$c114); } } if (s3 !== peg$FAILED) { while (s3 !== peg$FAILED) { s2.push(s3); - if (peg$c101.test(input.charAt(peg$currPos))) { + if (peg$c113.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c102); } + if (peg$silentFails === 0) { peg$fail(peg$c114); } } } } else { s2 = peg$FAILED; } if (s2 !== peg$FAILED) { - if (peg$c99.test(input.charAt(peg$currPos))) { + if (peg$c111.test(input.charAt(peg$currPos))) { s3 = input.charAt(peg$currPos); peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c100); } + if (peg$silentFails === 0) { peg$fail(peg$c112); } } if (s3 === peg$FAILED) { s3 = null; } if (s3 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c103(s2); + s1 = peg$c115(s2); s0 = s1; } else { peg$currPos = s0; @@ -1490,7 +1610,7 @@ export default (function() { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c98); } + if (peg$silentFails === 0) { peg$fail(peg$c110); } } return s0; @@ -1502,11 +1622,11 @@ export default (function() { peg$silentFails++; s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c105; + s1 = peg$c117; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c106); } + if (peg$silentFails === 0) { peg$fail(peg$c118); } } if (s1 !== peg$FAILED) { s2 = []; @@ -1517,15 +1637,15 @@ export default (function() { } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c105; + s3 = peg$c117; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c106); } + if (peg$silentFails === 0) { peg$fail(peg$c118); } } if (s3 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c107(s2); + s1 = peg$c119(s2); s0 = s1; } else { peg$currPos = s0; @@ -1542,11 +1662,11 @@ export default (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 39) { - s1 = peg$c108; + s1 = peg$c120; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c109); } + if (peg$silentFails === 0) { peg$fail(peg$c121); } } if (s1 !== peg$FAILED) { s2 = []; @@ -1557,15 +1677,15 @@ export default (function() { } if (s2 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 39) { - s3 = peg$c108; + s3 = peg$c120; peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c109); } + if (peg$silentFails === 0) { peg$fail(peg$c121); } } if (s3 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c107(s2); + s1 = peg$c119(s2); s0 = s1; } else { peg$currPos = s0; @@ -1604,7 +1724,7 @@ export default (function() { } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c107(s2); + s1 = peg$c119(s2); s0 = s1; } else { peg$currPos = s0; @@ -1619,7 +1739,7 @@ export default (function() { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c104); } + if (peg$silentFails === 0) { peg$fail(peg$c116); } } return s0; @@ -1631,12 +1751,12 @@ export default (function() { s0 = peg$currPos; s1 = peg$currPos; peg$silentFails++; - if (peg$c110.test(input.charAt(peg$currPos))) { + if (peg$c122.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c111); } + if (peg$silentFails === 0) { peg$fail(peg$c123); } } peg$silentFails--; if (s2 === peg$FAILED) { @@ -1651,11 +1771,11 @@ export default (function() { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c112); } + if (peg$silentFails === 0) { peg$fail(peg$c124); } } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c113(s2); + s1 = peg$c125(s2); s0 = s1; } else { peg$currPos = s0; @@ -1668,17 +1788,17 @@ export default (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c114; + s1 = peg$c126; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } + if (peg$silentFails === 0) { peg$fail(peg$c127); } } if (s1 !== peg$FAILED) { s2 = peg$parseEscapeSequence(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c113(s2); + s1 = peg$c125(s2); s0 = s1; } else { peg$currPos = s0; @@ -1699,12 +1819,12 @@ export default (function() { s0 = peg$currPos; s1 = peg$currPos; peg$silentFails++; - if (peg$c116.test(input.charAt(peg$currPos))) { + if (peg$c128.test(input.charAt(peg$currPos))) { s2 = input.charAt(peg$currPos); peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c117); } + if (peg$silentFails === 0) { peg$fail(peg$c129); } } peg$silentFails--; if (s2 === peg$FAILED) { @@ -1719,11 +1839,11 @@ export default (function() { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c112); } + if (peg$silentFails === 0) { peg$fail(peg$c124); } } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c113(s2); + s1 = peg$c125(s2); s0 = s1; } else { peg$currPos = s0; @@ -1736,17 +1856,17 @@ export default (function() { if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c114; + s1 = peg$c126; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c115); } + if (peg$silentFails === 0) { peg$fail(peg$c127); } } if (s1 !== peg$FAILED) { s2 = peg$parseEscapeSequence(); if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c113(s2); + s1 = peg$c125(s2); s0 = s1; } else { peg$currPos = s0; @@ -1781,11 +1901,11 @@ export default (function() { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c112); } + if (peg$silentFails === 0) { peg$fail(peg$c124); } } if (s2 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c113(s2); + s1 = peg$c125(s2); s0 = s1; } else { peg$currPos = s0; @@ -1802,53 +1922,53 @@ export default (function() { function peg$parseEscapeSequence() { var s0, s1; - if (peg$c118.test(input.charAt(peg$currPos))) { + if (peg$c130.test(input.charAt(peg$currPos))) { s0 = input.charAt(peg$currPos); peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c119); } + if (peg$silentFails === 0) { peg$fail(peg$c131); } } if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 110) { - s1 = peg$c120; + s1 = peg$c132; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c121); } + if (peg$silentFails === 0) { peg$fail(peg$c133); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c122(); + s1 = peg$c134(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 114) { - s1 = peg$c123; + s1 = peg$c135; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c124); } + if (peg$silentFails === 0) { peg$fail(peg$c136); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c125(); + s1 = peg$c137(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 116) { - s1 = peg$c126; + s1 = peg$c138; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c127); } + if (peg$silentFails === 0) { peg$fail(peg$c139); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$c128(); + s1 = peg$c140(); } s0 = s1; } @@ -1867,6 +1987,7 @@ export default (function() { orFilter.desc = first.desc + " or " + second.desc; return orFilter; } + function and(first, second) { function andFilter() { return first.apply(this, arguments) && second.apply(this, arguments); @@ -1874,6 +1995,7 @@ export default (function() { andFilter.desc = first.desc + " and " + second.desc; return andFilter; } + function not(expr) { function notFilter() { return !expr.apply(this, arguments); @@ -1881,6 +2003,7 @@ export default (function() { notFilter.desc = "not " + expr.desc; return notFilter; } + function binding(expr) { function bindingFilter() { return expr.apply(this, arguments); @@ -1888,22 +2011,20 @@ export default (function() { bindingFilter.desc = "(" + expr.desc + ")"; return bindingFilter; } - function trueFilter(flow) { + + // ~all + function allFilter(flow) { return true; } - trueFilter.desc = "true"; - function falseFilter(flow) { - return false; - } - falseFilter.desc = "false"; + allFilter.desc = "all flows"; + // ~a var ASSET_TYPES = [ new RegExp("text/javascript"), new RegExp("application/x-javascript"), new RegExp("application/javascript"), new RegExp("text/css"), - new RegExp("image/.*"), - new RegExp("application/x-shockwave-flash") + new RegExp("image/.*") ]; function assetFilter(flow) { if (flow.response) { @@ -1918,13 +2039,8 @@ export default (function() { return false; } assetFilter.desc = "is asset"; - function responseCode(code){ - function responseCodeFilter(flow){ - return flow.response && flow.response.status_code === code; - } - responseCodeFilter.desc = "resp. code is " + code; - return responseCodeFilter; - } + + // ~b function body(regex){ regex = new RegExp(regex, "i"); function bodyFilter(flow){ @@ -1933,6 +2049,8 @@ export default (function() { bodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10"; return bodyFilter; } + + // ~bq function requestBody(regex){ regex = new RegExp(regex, "i"); function requestBodyFilter(flow){ @@ -1941,6 +2059,8 @@ export default (function() { requestBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10"; return requestBodyFilter; } + + // ~bs function responseBody(regex){ regex = new RegExp(regex, "i"); function responseBodyFilter(flow){ @@ -1949,6 +2069,27 @@ export default (function() { responseBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10"; return responseBodyFilter; } + + // ~c + function responseCode(code){ + function responseCodeFilter(flow){ + return flow.response && flow.response.status_code === code; + } + responseCodeFilter.desc = "resp. code is " + code; + return responseCodeFilter; + } + + // ~comment + function comment(regex){ + regex = new RegExp(regex, "i"); + function commentFilter(flow){ + return regex.test(flow.comment) + } + commentFilter.desc = "comment matches " + regex; + return commentFilter; + } + + // ~d function domain(regex){ regex = new RegExp(regex, "i"); function domainFilter(flow){ @@ -1957,6 +2098,8 @@ export default (function() { domainFilter.desc = "domain matches " + regex; return domainFilter; } + + // ~dst function destination(regex){ regex = new RegExp(regex, "i"); function destinationFilter(flow){ @@ -1967,10 +2110,14 @@ export default (function() { destinationFilter.desc = "destination address matches " + regex; return destinationFilter; } + + // ~e function errorFilter(flow){ return !!flow.error; } errorFilter.desc = "has error"; + + // ~h function header(regex){ regex = new RegExp(regex, "i"); function headerFilter(flow){ @@ -1983,6 +2130,8 @@ export default (function() { headerFilter.desc = "header matches " + regex; return headerFilter; } + + // ~hq function requestHeader(regex){ regex = new RegExp(regex, "i"); function requestHeaderFilter(flow){ @@ -1991,6 +2140,8 @@ export default (function() { requestHeaderFilter.desc = "req. header matches " + regex; return requestHeaderFilter; } + + // ~hs function responseHeader(regex){ regex = new RegExp(regex, "i"); function responseHeaderFilter(flow){ @@ -1999,10 +2150,30 @@ export default (function() { responseHeaderFilter.desc = "resp. header matches " + regex; return responseHeaderFilter; } + + // ~http function httpFilter(flow){ return flow.type === "http"; } httpFilter.desc = "is an HTTP Flow"; + + // ~marked + function markedFilter(flow){ + return flow.marked; + } + markedFilter.desc = "is marked"; + + // ~marker + function marker(regex){ + regex = new RegExp(regex, "i"); + function markerFilter(flow){ + return regex.test(flow.marked) + } + markerFilter.desc = "marker matches " + regex; + return markerFilter; + } + + // ~m function method(regex){ regex = new RegExp(regex, "i"); function methodFilter(flow){ @@ -2011,18 +2182,32 @@ export default (function() { methodFilter.desc = "method matches " + regex; return methodFilter; } - function markedFilter(flow){ - return flow.marked; - } - markedFilter.desc = "is marked"; + + // ~q function noResponseFilter(flow){ return flow.request && !flow.response; } noResponseFilter.desc = "has no response"; - function responseFilter(flow){ - return !!flow.response; + + // ~replayq + function clientReplayFilter(flow){ + return flow.is_replay === "request"; } - responseFilter.desc = "has response"; + clientReplayFilter.desc = "request has been replayed"; + + // ~replays + function serverReplayFilter(flow){ + return flow.is_replay === "response"; + } + serverReplayFilter.desc = "response has been replayed"; + + // ~replay + function replayFilter(flow){ + return !!flow.is_replay; + } + replayFilter.desc = "flow has been replayed"; + + // ~src function source(regex){ regex = new RegExp(regex, "i"); function sourceFilter(flow){ @@ -2033,6 +2218,40 @@ export default (function() { sourceFilter.desc = "source address matches " + regex; return sourceFilter; } + + // ~s + function responseFilter(flow){ + return !!flow.response; + } + responseFilter.desc = "has response"; + + // ~tcp + function tcpFilter(flow){ + return flow.type === "tcp"; + } + tcpFilter.desc = "is a TCP Flow"; + + // ~tq + function requestContentType(regex){ + regex = new RegExp(regex, "i"); + function requestContentTypeFilter(flow){ + return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request)); + } + requestContentTypeFilter.desc = "req. content type matches " + regex; + return requestContentTypeFilter; + } + + // ~ts + function responseContentType(regex){ + regex = new RegExp(regex, "i"); + function responseContentTypeFilter(flow){ + return flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response)); + } + responseContentTypeFilter.desc = "resp. content type matches " + regex; + return responseContentTypeFilter; + } + + // ~t function contentType(regex){ regex = new RegExp(regex, "i"); function contentTypeFilter(flow){ @@ -2045,26 +2264,8 @@ export default (function() { contentTypeFilter.desc = "content type matches " + regex; return contentTypeFilter; } - function tcpFilter(flow){ - return flow.type === "tcp"; - } - tcpFilter.desc = "is a TCP Flow"; - function requestContentType(regex){ - regex = new RegExp(regex, "i"); - function requestContentTypeFilter(flow){ - return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request)); - } - requestContentTypeFilter.desc = "req. content type matches " + regex; - return requestContentTypeFilter; - } - function responseContentType(regex){ - regex = new RegExp(regex, "i"); - function responseContentTypeFilter(flow){ - return flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response)); - } - responseContentTypeFilter.desc = "resp. content type matches " + regex; - return responseContentTypeFilter; - } + + // ~u function url(regex){ regex = new RegExp(regex, "i"); function urlFilter(flow){ @@ -2073,6 +2274,8 @@ export default (function() { urlFilter.desc = "url matches " + regex; return urlFilter; } + + // ~websocket function websocketFilter(flow){ return flow.type === "websocket"; } diff --git a/web/src/js/filt/filt.peg b/web/src/js/filt/filt.peg index 987de5876..bca8bdddf 100644 --- a/web/src/js/filt/filt.peg +++ b/web/src/js/filt/filt.peg @@ -1,5 +1,4 @@ // PEG.js filter rules - see https://pegjs.org/ - { function or(first, second) { // Add explicit function names to ease debugging. @@ -9,6 +8,7 @@ function or(first, second) { orFilter.desc = first.desc + " or " + second.desc; return orFilter; } + function and(first, second) { function andFilter() { return first.apply(this, arguments) && second.apply(this, arguments); @@ -16,6 +16,7 @@ function and(first, second) { andFilter.desc = first.desc + " and " + second.desc; return andFilter; } + function not(expr) { function notFilter() { return !expr.apply(this, arguments); @@ -23,6 +24,7 @@ function not(expr) { notFilter.desc = "not " + expr.desc; return notFilter; } + function binding(expr) { function bindingFilter() { return expr.apply(this, arguments); @@ -30,22 +32,20 @@ function binding(expr) { bindingFilter.desc = "(" + expr.desc + ")"; return bindingFilter; } -function trueFilter(flow) { + +// ~all +function allFilter(flow) { return true; } -trueFilter.desc = "true"; -function falseFilter(flow) { - return false; -} -falseFilter.desc = "false"; +allFilter.desc = "all flows"; +// ~a var ASSET_TYPES = [ new RegExp("text/javascript"), new RegExp("application/x-javascript"), new RegExp("application/javascript"), new RegExp("text/css"), - new RegExp("image/.*"), - new RegExp("application/x-shockwave-flash") + new RegExp("image/.*") ]; function assetFilter(flow) { if (flow.response) { @@ -60,13 +60,8 @@ function assetFilter(flow) { return false; } assetFilter.desc = "is asset"; -function responseCode(code){ - function responseCodeFilter(flow){ - return flow.response && flow.response.status_code === code; - } - responseCodeFilter.desc = "resp. code is " + code; - return responseCodeFilter; -} + +// ~b function body(regex){ regex = new RegExp(regex, "i"); function bodyFilter(flow){ @@ -75,6 +70,8 @@ function body(regex){ bodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10"; return bodyFilter; } + +// ~bq function requestBody(regex){ regex = new RegExp(regex, "i"); function requestBodyFilter(flow){ @@ -83,6 +80,8 @@ function requestBody(regex){ requestBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10"; return requestBodyFilter; } + +// ~bs function responseBody(regex){ regex = new RegExp(regex, "i"); function responseBodyFilter(flow){ @@ -91,6 +90,27 @@ function responseBody(regex){ responseBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10"; return responseBodyFilter; } + +// ~c +function responseCode(code){ + function responseCodeFilter(flow){ + return flow.response && flow.response.status_code === code; + } + responseCodeFilter.desc = "resp. code is " + code; + return responseCodeFilter; +} + +// ~comment +function comment(regex){ + regex = new RegExp(regex, "i"); + function commentFilter(flow){ + return regex.test(flow.comment) + } + commentFilter.desc = "comment matches " + regex; + return commentFilter; +} + +// ~d function domain(regex){ regex = new RegExp(regex, "i"); function domainFilter(flow){ @@ -99,6 +119,8 @@ function domain(regex){ domainFilter.desc = "domain matches " + regex; return domainFilter; } + +// ~dst function destination(regex){ regex = new RegExp(regex, "i"); function destinationFilter(flow){ @@ -109,10 +131,14 @@ function destination(regex){ destinationFilter.desc = "destination address matches " + regex; return destinationFilter; } + +// ~e function errorFilter(flow){ return !!flow.error; } errorFilter.desc = "has error"; + +// ~h function header(regex){ regex = new RegExp(regex, "i"); function headerFilter(flow){ @@ -125,6 +151,8 @@ function header(regex){ headerFilter.desc = "header matches " + regex; return headerFilter; } + +// ~hq function requestHeader(regex){ regex = new RegExp(regex, "i"); function requestHeaderFilter(flow){ @@ -133,6 +161,8 @@ function requestHeader(regex){ requestHeaderFilter.desc = "req. header matches " + regex; return requestHeaderFilter; } + +// ~hs function responseHeader(regex){ regex = new RegExp(regex, "i"); function responseHeaderFilter(flow){ @@ -141,10 +171,30 @@ function responseHeader(regex){ responseHeaderFilter.desc = "resp. header matches " + regex; return responseHeaderFilter; } + +// ~http function httpFilter(flow){ return flow.type === "http"; } httpFilter.desc = "is an HTTP Flow"; + +// ~marked +function markedFilter(flow){ + return flow.marked; +} +markedFilter.desc = "is marked"; + +// ~marker +function marker(regex){ + regex = new RegExp(regex, "i"); + function markerFilter(flow){ + return regex.test(flow.marked) + } + markerFilter.desc = "marker matches " + regex; + return markerFilter; +} + +// ~m function method(regex){ regex = new RegExp(regex, "i"); function methodFilter(flow){ @@ -153,18 +203,32 @@ function method(regex){ methodFilter.desc = "method matches " + regex; return methodFilter; } -function markedFilter(flow){ - return flow.marked; -} -markedFilter.desc = "is marked"; + +// ~q function noResponseFilter(flow){ return flow.request && !flow.response; } noResponseFilter.desc = "has no response"; -function responseFilter(flow){ - return !!flow.response; + +// ~replayq +function clientReplayFilter(flow){ + return flow.is_replay === "request"; } -responseFilter.desc = "has response"; +clientReplayFilter.desc = "request has been replayed"; + +// ~replays +function serverReplayFilter(flow){ + return flow.is_replay === "response"; +} +serverReplayFilter.desc = "response has been replayed"; + +// ~replay +function replayFilter(flow){ + return !!flow.is_replay; +} +replayFilter.desc = "flow has been replayed"; + +// ~src function source(regex){ regex = new RegExp(regex, "i"); function sourceFilter(flow){ @@ -175,6 +239,40 @@ function source(regex){ sourceFilter.desc = "source address matches " + regex; return sourceFilter; } + +// ~s +function responseFilter(flow){ + return !!flow.response; +} +responseFilter.desc = "has response"; + +// ~tcp +function tcpFilter(flow){ + return flow.type === "tcp"; +} +tcpFilter.desc = "is a TCP Flow"; + +// ~tq +function requestContentType(regex){ + regex = new RegExp(regex, "i"); + function requestContentTypeFilter(flow){ + return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request)); + } + requestContentTypeFilter.desc = "req. content type matches " + regex; + return requestContentTypeFilter; +} + +// ~ts +function responseContentType(regex){ + regex = new RegExp(regex, "i"); + function responseContentTypeFilter(flow){ + return flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response)); + } + responseContentTypeFilter.desc = "resp. content type matches " + regex; + return responseContentTypeFilter; +} + +// ~t function contentType(regex){ regex = new RegExp(regex, "i"); function contentTypeFilter(flow){ @@ -187,26 +285,8 @@ function contentType(regex){ contentTypeFilter.desc = "content type matches " + regex; return contentTypeFilter; } -function tcpFilter(flow){ - return flow.type === "tcp"; -} -tcpFilter.desc = "is a TCP Flow"; -function requestContentType(regex){ - regex = new RegExp(regex, "i"); - function requestContentTypeFilter(flow){ - return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request)); - } - requestContentTypeFilter.desc = "req. content type matches " + regex; - return requestContentTypeFilter; -} -function responseContentType(regex){ - regex = new RegExp(regex, "i"); - function responseContentTypeFilter(flow){ - return flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response)); - } - responseContentTypeFilter.desc = "resp. content type matches " + regex; - return responseContentTypeFilter; -} + +// ~u function url(regex){ regex = new RegExp(regex, "i"); function urlFilter(flow){ @@ -215,6 +295,8 @@ function url(regex){ urlFilter.desc = "url matches " + regex; return urlFilter; } + +// ~websocket function websocketFilter(flow){ return flow.type === "websocket"; } @@ -250,19 +332,18 @@ BindingExpr { return binding(expr); } / Expr -/* All the filters except "~s" and "~src" are arranged in the ascending order as - given in the docs(https://mitmproxy.org/docs/latest/concepts-filters/). - "~s" and "~src" are so arranged as "~s" caused problems in the evaluation of - "~src". */ +/* All the filters are generally arranged in the order as they are described + on https://docs.mitmproxy.org/dev/concepts-filters/, with the exception of + single-letter filters, which are moved to the bottom so that they evaluate properly */ Expr - = "true" { return trueFilter; } - / "false" { return falseFilter; } + = "~all" { return allFilter; } / "~a" { return assetFilter; } / "~b" ws+ s:StringLiteral { return body(s); } / "~bq" ws+ s:StringLiteral { return requestBody(s); } / "~bs" ws+ s:StringLiteral { return responseBody(s); } / "~c" ws+ s:IntegerLiteral { return responseCode(s); } + / "~comment" ws+ s:StringLiteral { return comment(s); } / "~d" ws+ s:StringLiteral { return domain(s); } / "~dst" ws+ s:StringLiteral { return destination(s); } / "~e" { return errorFilter; } @@ -270,15 +351,19 @@ Expr / "~hq" ws+ s:StringLiteral { return requestHeader(s); } / "~hs" ws+ s:StringLiteral { return responseHeader(s); } / "~http" { return httpFilter; } - / "~m" ws+ s:StringLiteral { return method(s); } / "~marked" { return markedFilter; } + / "~marker" ws+ s:StringLiteral { return marker(s); } + / "~m" ws+ s:StringLiteral { return method(s); } / "~q" { return noResponseFilter; } + / "~replayq" { return clientReplayFilter; } + / "~replays" { return serverReplayFilter; } + / "~replay" { return replayFilter; } / "~src" ws+ s:StringLiteral { return source(s); } / "~s" { return responseFilter; } - / "~t" ws+ s:StringLiteral { return contentType(s); } / "~tcp" { return tcpFilter; } / "~tq" ws+ s:StringLiteral { return requestContentType(s); } / "~ts" ws+ s:StringLiteral { return responseContentType(s); } + / "~t" ws+ s:StringLiteral { return contentType(s); } / "~u" ws+ s:StringLiteral { return url(s); } / "~websocket" { return websocketFilter; } / s:StringLiteral { return url(s); } diff --git a/web/src/js/flow.ts b/web/src/js/flow.ts index 5f79425d0..1d46b027d 100644 --- a/web/src/js/flow.ts +++ b/web/src/js/flow.ts @@ -8,6 +8,7 @@ interface _Flow { type: string modified: boolean marked: string + comment: string client_conn: Client server_conn?: Server error?: Error diff --git a/web/src/js/urlState.ts b/web/src/js/urlState.ts index 4bb8796f4..cd82d0436 100644 --- a/web/src/js/urlState.ts +++ b/web/src/js/urlState.ts @@ -33,7 +33,8 @@ export function updateStoreFromUrl(store) { query .split("&") .forEach((x) => { - const [key, value] = x.split("=", 2) + let [key, value] = x.split("=", 2) + value = decodeURIComponent(value) switch (key) { case Query.SEARCH: store.dispatch(setFilter(value)) @@ -66,7 +67,7 @@ export function updateUrlFromStore(store) { } const queryStr = Object.keys(query) .filter(k => query[k]) - .map(k => `${k}=${query[k]}`) + .map(k => `${k}=${encodeURIComponent(query[k])}`) .join("&") let url