diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/mitmproxy/builtins/test_clientplayback.py | 37 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_script.py | 25 | ||||
-rw-r--r-- | test/mitmproxy/builtins/test_serverplayback.py | 284 | ||||
-rw-r--r-- | test/mitmproxy/console/test_master.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/mastertest.py | 21 | ||||
-rw-r--r-- | test/mitmproxy/protocol/test_http1.py | 12 | ||||
-rw-r--r-- | test/mitmproxy/protocol/test_http2.py | 49 | ||||
-rw-r--r-- | test/mitmproxy/test_addons.py | 4 | ||||
-rw-r--r-- | test/mitmproxy/test_dump.py | 11 | ||||
-rw-r--r-- | test/mitmproxy/test_flow.py | 358 | ||||
-rw-r--r-- | test/mitmproxy/test_fuzzing.py | 9 | ||||
-rw-r--r-- | test/mitmproxy/test_server.py | 337 | ||||
-rw-r--r-- | test/mitmproxy/tservers.py | 49 | ||||
-rw-r--r-- | test/netlib/http/test_headers.py | 9 | ||||
-rw-r--r-- | test/netlib/http/test_response.py | 5 | ||||
-rw-r--r-- | test/netlib/test_encoding.py | 59 | ||||
-rw-r--r-- | test/netlib/test_strutils.py | 2 | ||||
-rw-r--r-- | test/pathod/test_protocols_http2.py | 6 |
18 files changed, 678 insertions, 601 deletions
diff --git a/test/mitmproxy/builtins/test_clientplayback.py b/test/mitmproxy/builtins/test_clientplayback.py new file mode 100644 index 00000000..15702340 --- /dev/null +++ b/test/mitmproxy/builtins/test_clientplayback.py @@ -0,0 +1,37 @@ +import mock + +from mitmproxy.builtins import clientplayback +from mitmproxy import options + +from .. import tutils, mastertest + + +class TestClientPlayback: + def test_playback(self): + cp = clientplayback.ClientPlayback() + cp.configure(options.Options(), []) + assert cp.count() == 0 + f = tutils.tflow(resp=True) + cp.load([f]) + assert cp.count() == 1 + RP = "mitmproxy.protocol.http_replay.RequestReplayThread" + with mock.patch(RP) as rp: + assert not cp.current + with mastertest.mockctx(): + cp.tick() + rp.assert_called() + assert cp.current + + cp.keepserving = False + cp.flows = None + cp.current = None + with mock.patch("mitmproxy.controller.Master.shutdown") as sd: + with mastertest.mockctx(): + cp.tick() + sd.assert_called() + + def test_configure(self): + cp = clientplayback.ClientPlayback() + cp.configure( + options.Options(), [] + ) diff --git a/test/mitmproxy/builtins/test_script.py b/test/mitmproxy/builtins/test_script.py index 0bac6ca0..09e5bc92 100644 --- a/test/mitmproxy/builtins/test_script.py +++ b/test/mitmproxy/builtins/test_script.py @@ -137,6 +137,31 @@ class TestScript(mastertest.MasterTest): class TestScriptLoader(mastertest.MasterTest): + def test_run_once(self): + s = state.State() + o = options.Options(scripts=[]) + m = master.FlowMaster(o, None, s) + sl = script.ScriptLoader() + m.addons.add(o, sl) + + f = tutils.tflow(resp=True) + with m.handlecontext(): + sc = sl.run_once( + tutils.test_data.path( + "data/addonscripts/recorder.py" + ), [f] + ) + evts = [i[1] for i in sc.ns.call_log] + assert evts == ['start', 'request', 'responseheaders', 'response', 'done'] + + with m.handlecontext(): + tutils.raises( + "file not found", + sl.run_once, + "nonexistent", + [f] + ) + def test_simple(self): s = state.State() o = options.Options(scripts=[]) diff --git a/test/mitmproxy/builtins/test_serverplayback.py b/test/mitmproxy/builtins/test_serverplayback.py new file mode 100644 index 00000000..4db509da --- /dev/null +++ b/test/mitmproxy/builtins/test_serverplayback.py @@ -0,0 +1,284 @@ +from .. import tutils, mastertest + +import netlib.tutils +from mitmproxy.builtins import serverplayback +from mitmproxy import options +from mitmproxy import exceptions +from mitmproxy import flow + + +class TestServerPlayback: + def test_server_playback(self): + sp = serverplayback.ServerPlayback() + sp.configure(options.Options(), []) + f = tutils.tflow(resp=True) + + assert not sp.flowmap + + sp.load([f]) + assert sp.flowmap + assert sp.next_flow(f) + assert not sp.flowmap + + def test_ignore_host(self): + sp = serverplayback.ServerPlayback() + sp.configure(options.Options(server_replay_ignore_host=True), []) + + r = tutils.tflow(resp=True) + r2 = tutils.tflow(resp=True) + + r.request.host = "address" + r2.request.host = "address" + assert sp._hash(r) == sp._hash(r2) + r2.request.host = "wrong_address" + assert sp._hash(r) == sp._hash(r2) + + def test_ignore_content(self): + s = serverplayback.ServerPlayback() + s.configure(options.Options(server_replay_ignore_content=False), []) + + r = tutils.tflow(resp=True) + r2 = tutils.tflow(resp=True) + + r.request.content = b"foo" + r2.request.content = b"foo" + assert s._hash(r) == s._hash(r2) + r2.request.content = b"bar" + assert not s._hash(r) == s._hash(r2) + + s.configure(options.Options(server_replay_ignore_content=True), []) + r = tutils.tflow(resp=True) + r2 = tutils.tflow(resp=True) + r.request.content = b"foo" + r2.request.content = b"foo" + assert s._hash(r) == s._hash(r2) + r2.request.content = b"bar" + assert s._hash(r) == s._hash(r2) + r2.request.content = b"" + assert s._hash(r) == s._hash(r2) + r2.request.content = None + assert s._hash(r) == s._hash(r2) + + def test_ignore_content_wins_over_params(self): + s = serverplayback.ServerPlayback() + s.configure( + options.Options( + server_replay_ignore_content=True, + server_replay_ignore_payload_params=[ + "param1", "param2" + ] + ), + [] + ) + # NOTE: parameters are mutually exclusive in options + + r = tutils.tflow(resp=True) + r.request.headers["Content-Type"] = "application/x-www-form-urlencoded" + r.request.content = b"paramx=y" + + r2 = tutils.tflow(resp=True) + r2.request.headers["Content-Type"] = "application/x-www-form-urlencoded" + r2.request.content = b"paramx=x" + + # same parameters + assert s._hash(r) == s._hash(r2) + + def test_ignore_payload_params_other_content_type(self): + s = serverplayback.ServerPlayback() + s.configure( + options.Options( + server_replay_ignore_content=False, + server_replay_ignore_payload_params=[ + "param1", "param2" + ] + ), + [] + + ) + r = tutils.tflow(resp=True) + r.request.headers["Content-Type"] = "application/json" + r.request.content = b'{"param1":"1"}' + r2 = tutils.tflow(resp=True) + r2.request.headers["Content-Type"] = "application/json" + r2.request.content = b'{"param1":"1"}' + # same content + assert s._hash(r) == s._hash(r2) + # distint content (note only x-www-form-urlencoded payload is analysed) + r2.request.content = b'{"param1":"2"}' + assert not s._hash(r) == s._hash(r2) + + def test_hash(self): + s = serverplayback.ServerPlayback() + s.configure(options.Options(), []) + + r = tutils.tflow() + r2 = tutils.tflow() + + assert s._hash(r) + assert s._hash(r) == s._hash(r2) + r.request.headers["foo"] = "bar" + assert s._hash(r) == s._hash(r2) + r.request.path = "voing" + assert s._hash(r) != s._hash(r2) + + r.request.path = "path?blank_value" + r2.request.path = "path?" + assert s._hash(r) != s._hash(r2) + + def test_headers(self): + s = serverplayback.ServerPlayback() + s.configure(options.Options(server_replay_use_headers=["foo"]), []) + + r = tutils.tflow(resp=True) + r.request.headers["foo"] = "bar" + r2 = tutils.tflow(resp=True) + assert not s._hash(r) == s._hash(r2) + r2.request.headers["foo"] = "bar" + assert s._hash(r) == s._hash(r2) + r2.request.headers["oink"] = "bar" + assert s._hash(r) == s._hash(r2) + + r = tutils.tflow(resp=True) + r2 = tutils.tflow(resp=True) + assert s._hash(r) == s._hash(r2) + + def test_load(self): + s = serverplayback.ServerPlayback() + s.configure(options.Options(), []) + + r = tutils.tflow(resp=True) + r.request.headers["key"] = "one" + + r2 = tutils.tflow(resp=True) + r2.request.headers["key"] = "two" + + s.load([r, r2]) + + assert s.count() == 2 + + n = s.next_flow(r) + assert n.request.headers["key"] == "one" + assert s.count() == 1 + + n = s.next_flow(r) + assert n.request.headers["key"] == "two" + assert not s.flowmap + assert s.count() == 0 + + assert not s.next_flow(r) + + def test_load_with_server_replay_nopop(self): + s = serverplayback.ServerPlayback() + s.configure(options.Options(server_replay_nopop=True), []) + + r = tutils.tflow(resp=True) + r.request.headers["key"] = "one" + + r2 = tutils.tflow(resp=True) + r2.request.headers["key"] = "two" + + s.load([r, r2]) + + assert s.count() == 2 + s.next_flow(r) + assert s.count() == 2 + + def test_ignore_params(self): + s = serverplayback.ServerPlayback() + s.configure( + options.Options( + server_replay_ignore_params=["param1", "param2"] + ), + [] + ) + + r = tutils.tflow(resp=True) + r.request.path = "/test?param1=1" + r2 = tutils.tflow(resp=True) + r2.request.path = "/test" + assert s._hash(r) == s._hash(r2) + r2.request.path = "/test?param1=2" + assert s._hash(r) == s._hash(r2) + r2.request.path = "/test?param2=1" + assert s._hash(r) == s._hash(r2) + r2.request.path = "/test?param3=2" + assert not s._hash(r) == s._hash(r2) + + def test_ignore_payload_params(self): + s = serverplayback.ServerPlayback() + s.configure( + options.Options( + server_replay_ignore_payload_params=["param1", "param2"] + ), + [] + ) + + r = tutils.tflow(resp=True) + r.request.headers["Content-Type"] = "application/x-www-form-urlencoded" + r.request.content = b"paramx=x¶m1=1" + r2 = tutils.tflow(resp=True) + r2.request.headers["Content-Type"] = "application/x-www-form-urlencoded" + r2.request.content = b"paramx=x¶m1=1" + # same parameters + assert s._hash(r) == s._hash(r2) + # ignored parameters != + r2.request.content = b"paramx=x¶m1=2" + assert s._hash(r) == s._hash(r2) + # missing parameter + r2.request.content = b"paramx=x" + assert s._hash(r) == s._hash(r2) + # ignorable parameter added + r2.request.content = b"paramx=x¶m1=2" + assert s._hash(r) == s._hash(r2) + # not ignorable parameter changed + r2.request.content = b"paramx=y¶m1=1" + assert not s._hash(r) == s._hash(r2) + # not ignorable parameter missing + r2.request.content = b"param1=1" + assert not s._hash(r) == s._hash(r2) + + def test_server_playback_full(self): + state = flow.State() + s = serverplayback.ServerPlayback() + o = options.Options(refresh_server_playback = True, keepserving=False) + m = mastertest.RecordingMaster(o, None, state) + m.addons.add(o, s) + + f = tutils.tflow() + f.response = netlib.tutils.tresp(content=f.request.content) + s.load([f, f]) + + tf = tutils.tflow() + assert not tf.response + m.request(tf) + assert tf.response == f.response + + tf = tutils.tflow() + tf.request.content = b"gibble" + assert not tf.response + m.request(tf) + assert not tf.response + + assert not s.stop + s.tick() + assert not s.stop + + tf = tutils.tflow() + m.request(tutils.tflow()) + assert s.stop + + def test_server_playback_kill(self): + state = flow.State() + s = serverplayback.ServerPlayback() + o = options.Options(refresh_server_playback = True, replay_kill_extra=True) + m = mastertest.RecordingMaster(o, None, state) + m.addons.add(o, s) + + f = tutils.tflow() + f.response = netlib.tutils.tresp(content=f.request.content) + s.load([f]) + + f = tutils.tflow() + f.request.host = "nonexistent" + m.request(f) + assert f.reply.value == exceptions.Kill diff --git a/test/mitmproxy/console/test_master.py b/test/mitmproxy/console/test_master.py index fcb87e1b..8388a6bd 100644 --- a/test/mitmproxy/console/test_master.py +++ b/test/mitmproxy/console/test_master.py @@ -107,7 +107,7 @@ def test_format_keyvals(): def test_options(): - assert console.master.Options(kill=True) + assert console.master.Options(replay_kill_extra=True) class TestMaster(mastertest.MasterTest): diff --git a/test/mitmproxy/mastertest.py b/test/mitmproxy/mastertest.py index 08659d19..a14fe02a 100644 --- a/test/mitmproxy/mastertest.py +++ b/test/mitmproxy/mastertest.py @@ -1,8 +1,14 @@ +import contextlib + from . import tutils import netlib.tutils from mitmproxy.flow import master -from mitmproxy import flow, proxy, models, controller +from mitmproxy import flow, proxy, models, controller, options + + +class TestMaster: + pass class MasterTest: @@ -16,7 +22,9 @@ class MasterTest: master.serverconnect(f.server_conn) master.request(f) if not f.error: - f.response = models.HTTPResponse.wrap(netlib.tutils.tresp(content=content)) + f.response = models.HTTPResponse.wrap( + netlib.tutils.tresp(content=content) + ) master.response(f) master.clientdisconnect(f) return f @@ -41,3 +49,12 @@ class RecordingMaster(master.FlowMaster): def add_log(self, e, level): self.event_log.append((level, e)) + + +@contextlib.contextmanager +def mockctx(): + state = flow.State() + o = options.Options(refresh_server_playback = True, keepserving=False) + m = RecordingMaster(o, proxy.DummyServer(o), state) + with m.handlecontext(): + yield diff --git a/test/mitmproxy/protocol/test_http1.py b/test/mitmproxy/protocol/test_http1.py index 7d04c56b..2fc4ac63 100644 --- a/test/mitmproxy/protocol/test_http1.py +++ b/test/mitmproxy/protocol/test_http1.py @@ -18,14 +18,15 @@ class TestInvalidRequests(tservers.HTTPProxyTest): def test_double_connect(self): p = self.pathoc() - r = p.request("connect:'%s:%s'" % ("127.0.0.1", self.server2.port)) + with p.connect(): + r = p.request("connect:'%s:%s'" % ("127.0.0.1", self.server2.port)) assert r.status_code == 400 assert b"Invalid HTTP request form" in r.content def test_relative_request(self): p = self.pathoc_raw() - p.connect() - r = p.request("get:/p/200") + with p.connect(): + r = p.request("get:/p/200") assert r.status_code == 400 assert b"Invalid HTTP request form" in r.content @@ -61,5 +62,8 @@ class TestHeadContentLength(tservers.HTTPProxyTest): def test_head_content_length(self): p = self.pathoc() - resp = p.request("""head:'%s/p/200:h"Content-Length"="42"'""" % self.server.urlbase) + with p.connect(): + resp = p.request( + """head:'%s/p/200:h"Content-Length"="42"'""" % self.server.urlbase + ) assert resp.headers["Content-Length"] == "42" diff --git a/test/mitmproxy/protocol/test_http2.py b/test/mitmproxy/protocol/test_http2.py index 1eabebf1..c4bd2049 100644 --- a/test/mitmproxy/protocol/test_http2.py +++ b/test/mitmproxy/protocol/test_http2.py @@ -15,7 +15,7 @@ from mitmproxy.proxy.config import ProxyConfig import netlib from ...netlib import tservers as netlib_tservers from netlib.exceptions import HttpException -from netlib.http.http2 import framereader +from netlib.http import http1, http2 from .. import tservers @@ -33,6 +33,11 @@ requires_alpn = pytest.mark.skipif( reason='requires OpenSSL with ALPN support') +# inspect the log: +# for msg in self.proxy.tmaster.tlog: +# print(msg) + + class _Http2ServerBase(netlib_tservers.ServerTestBase): ssl = dict(alpn_select=b'h2') @@ -55,7 +60,7 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(self.rfile)) + raw = b''.join(http2.read_raw_frame(self.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -124,11 +129,17 @@ class _Http2TestBase(object): client.connect() # send CONNECT request - client.wfile.write( - b"CONNECT localhost:%d HTTP/1.1\r\n" - b"Host: localhost:%d\r\n" - b"\r\n" % (self.server.server.address.port, self.server.server.address.port) - ) + client.wfile.write(http1.assemble_request(netlib.http.Request( + 'authority', + b'CONNECT', + b'', + b'localhost', + self.server.server.address.port, + b'/', + b'HTTP/1.1', + [(b'host', b'localhost:%d' % self.server.server.address.port)], + b'', + ))) client.wfile.flush() # read CONNECT response @@ -242,7 +253,7 @@ class TestSimple(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -319,7 +330,7 @@ class TestRequestWithPriority(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -358,7 +369,7 @@ class TestRequestWithPriority(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -430,7 +441,7 @@ class TestPriority(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -507,7 +518,7 @@ class TestPriorityWithExistingStream(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -558,7 +569,7 @@ class TestStreamResetFromServer(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -608,7 +619,7 @@ class TestBodySizeLimit(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -693,7 +704,7 @@ class TestPushPromise(_Http2Test): responses = 0 while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -746,7 +757,7 @@ class TestPushPromise(_Http2Test): responses = 0 while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -806,7 +817,7 @@ class TestConnectionLost(_Http2Test): done = False while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) h2_conn.receive_data(raw) except HttpException: print(traceback.format_exc()) @@ -863,7 +874,7 @@ class TestMaxConcurrentStreams(_Http2Test): ended_streams = 0 while ended_streams != len(new_streams): try: - header, body = framereader.http2_read_raw_frame(client.rfile) + header, body = http2.read_raw_frame(client.rfile) events = h2_conn.receive_data(b''.join([header, body])) except: break @@ -909,7 +920,7 @@ class TestConnectionTerminated(_Http2Test): connection_terminated_event = None while not done: try: - raw = b''.join(framereader.http2_read_raw_frame(client.rfile)) + raw = b''.join(http2.read_raw_frame(client.rfile)) events = h2_conn.receive_data(raw) for event in events: if isinstance(event, h2.events.ConnectionTerminated): diff --git a/test/mitmproxy/test_addons.py b/test/mitmproxy/test_addons.py index a5085ea0..52d7f07f 100644 --- a/test/mitmproxy/test_addons.py +++ b/test/mitmproxy/test_addons.py @@ -17,5 +17,5 @@ def test_simple(): m = controller.Master(o) a = addons.Addons(m) a.add(o, TAddon("one")) - assert a.has_addon("one") - assert not a.has_addon("two") + assert a.get("one") + assert not a.get("two") diff --git a/test/mitmproxy/test_dump.py b/test/mitmproxy/test_dump.py index 90f33264..06f39e9d 100644 --- a/test/mitmproxy/test_dump.py +++ b/test/mitmproxy/test_dump.py @@ -45,18 +45,17 @@ class TestDumpMaster(mastertest.MasterTest): m = dump.DumpMaster(None, o) f = tutils.tflow(err=True) m.error(f) - assert m.error(f) assert "error" in o.tfile.getvalue() def test_replay(self): - o = dump.Options(server_replay=["nonexistent"], kill=True) - tutils.raises(dump.DumpError, dump.DumpMaster, None, o) + o = dump.Options(server_replay=["nonexistent"], replay_kill_extra=True) + tutils.raises(exceptions.OptionsError, dump.DumpMaster, None, o) with tutils.tmpdir() as t: p = os.path.join(t, "rep") self.flowfile(p) - o = dump.Options(server_replay=[p], kill=True) + o = dump.Options(server_replay=[p], replay_kill_extra=True) o.verbosity = 0 o.flow_detail = 0 m = dump.DumpMaster(None, o) @@ -64,13 +63,13 @@ class TestDumpMaster(mastertest.MasterTest): self.cycle(m, b"content") self.cycle(m, b"content") - o = dump.Options(server_replay=[p], kill=False) + o = dump.Options(server_replay=[p], replay_kill_extra=False) o.verbosity = 0 o.flow_detail = 0 m = dump.DumpMaster(None, o) self.cycle(m, b"nonexistent") - o = dump.Options(client_replay=[p], kill=False) + o = dump.Options(client_replay=[p], replay_kill_extra=False) o.verbosity = 0 o.flow_detail = 0 m = dump.DumpMaster(None, o) diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index 1caeb100..0fe45afb 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -37,261 +37,6 @@ def test_app_registry(): assert ar.get(r) -class TestClientPlaybackState: - - def test_tick(self): - first = tutils.tflow() - s = flow.State() - fm = flow.FlowMaster(None, None, s) - fm.start_client_playback([first, tutils.tflow()], True) - c = fm.client_playback - c.testing = True - - assert not c.done() - assert not s.flow_count() - assert c.count() == 2 - c.tick(fm) - assert s.flow_count() - assert c.count() == 1 - - c.tick(fm) - assert c.count() == 1 - - c.clear(c.current) - c.tick(fm) - assert c.count() == 0 - c.clear(c.current) - assert c.done() - - fm.state.clear() - fm.tick(timeout=0) - - fm.stop_client_playback() - assert not fm.client_playback - - -class TestServerPlaybackState: - - def test_hash(self): - s = flow.ServerPlaybackState( - None, - [], - False, - False, - None, - False, - None, - False) - r = tutils.tflow() - r2 = tutils.tflow() - - assert s._hash(r) - assert s._hash(r) == s._hash(r2) - r.request.headers["foo"] = "bar" - assert s._hash(r) == s._hash(r2) - r.request.path = "voing" - assert s._hash(r) != s._hash(r2) - - r.request.path = "path?blank_value" - r2.request.path = "path?" - assert s._hash(r) != s._hash(r2) - - def test_headers(self): - s = flow.ServerPlaybackState( - ["foo"], - [], - False, - False, - None, - False, - None, - False) - r = tutils.tflow(resp=True) - r.request.headers["foo"] = "bar" - r2 = tutils.tflow(resp=True) - assert not s._hash(r) == s._hash(r2) - r2.request.headers["foo"] = "bar" - assert s._hash(r) == s._hash(r2) - r2.request.headers["oink"] = "bar" - assert s._hash(r) == s._hash(r2) - - r = tutils.tflow(resp=True) - r2 = tutils.tflow(resp=True) - assert s._hash(r) == s._hash(r2) - - def test_load(self): - r = tutils.tflow(resp=True) - r.request.headers["key"] = "one" - - r2 = tutils.tflow(resp=True) - r2.request.headers["key"] = "two" - - s = flow.ServerPlaybackState( - None, [ - r, r2], False, False, None, False, None, False) - assert s.count() == 2 - assert len(s.fmap.keys()) == 1 - - n = s.next_flow(r) - assert n.request.headers["key"] == "one" - assert s.count() == 1 - - n = s.next_flow(r) - assert n.request.headers["key"] == "two" - assert s.count() == 0 - - assert not s.next_flow(r) - - def test_load_with_nopop(self): - r = tutils.tflow(resp=True) - r.request.headers["key"] = "one" - - r2 = tutils.tflow(resp=True) - r2.request.headers["key"] = "two" - - s = flow.ServerPlaybackState( - None, [ - r, r2], False, True, None, False, None, False) - - assert s.count() == 2 - s.next_flow(r) - assert s.count() == 2 - - def test_ignore_params(self): - s = flow.ServerPlaybackState( - None, [], False, False, [ - "param1", "param2"], False, None, False) - r = tutils.tflow(resp=True) - r.request.path = "/test?param1=1" - r2 = tutils.tflow(resp=True) - r2.request.path = "/test" - assert s._hash(r) == s._hash(r2) - r2.request.path = "/test?param1=2" - assert s._hash(r) == s._hash(r2) - r2.request.path = "/test?param2=1" - assert s._hash(r) == s._hash(r2) - r2.request.path = "/test?param3=2" - assert not s._hash(r) == s._hash(r2) - - def test_ignore_payload_params(self): - s = flow.ServerPlaybackState( - None, [], False, False, None, False, [ - "param1", "param2"], False) - r = tutils.tflow(resp=True) - r.request.headers["Content-Type"] = "application/x-www-form-urlencoded" - r.request.content = b"paramx=x¶m1=1" - r2 = tutils.tflow(resp=True) - r2.request.headers["Content-Type"] = "application/x-www-form-urlencoded" - r2.request.content = b"paramx=x¶m1=1" - # same parameters - assert s._hash(r) == s._hash(r2) - # ignored parameters != - r2.request.content = b"paramx=x¶m1=2" - assert s._hash(r) == s._hash(r2) - # missing parameter - r2.request.content = b"paramx=x" - assert s._hash(r) == s._hash(r2) - # ignorable parameter added - r2.request.content = b"paramx=x¶m1=2" - assert s._hash(r) == s._hash(r2) - # not ignorable parameter changed - r2.request.content = b"paramx=y¶m1=1" - assert not s._hash(r) == s._hash(r2) - # not ignorable parameter missing - r2.request.content = b"param1=1" - assert not s._hash(r) == s._hash(r2) - - def test_ignore_payload_params_other_content_type(self): - s = flow.ServerPlaybackState( - None, [], False, False, None, False, [ - "param1", "param2"], False) - r = tutils.tflow(resp=True) - r.request.headers["Content-Type"] = "application/json" - r.request.content = b'{"param1":"1"}' - r2 = tutils.tflow(resp=True) - r2.request.headers["Content-Type"] = "application/json" - r2.request.content = b'{"param1":"1"}' - # same content - assert s._hash(r) == s._hash(r2) - # distint content (note only x-www-form-urlencoded payload is analysed) - r2.request.content = b'{"param1":"2"}' - assert not s._hash(r) == s._hash(r2) - - def test_ignore_payload_wins_over_params(self): - # NOTE: parameters are mutually exclusive in options - s = flow.ServerPlaybackState( - None, [], False, False, None, True, [ - "param1", "param2"], False) - r = tutils.tflow(resp=True) - r.request.headers["Content-Type"] = "application/x-www-form-urlencoded" - r.request.content = b"paramx=y" - r2 = tutils.tflow(resp=True) - r2.request.headers["Content-Type"] = "application/x-www-form-urlencoded" - r2.request.content = b"paramx=x" - # same parameters - assert s._hash(r) == s._hash(r2) - - def test_ignore_content(self): - s = flow.ServerPlaybackState( - None, - [], - False, - False, - None, - False, - None, - False) - r = tutils.tflow(resp=True) - r2 = tutils.tflow(resp=True) - - r.request.content = b"foo" - r2.request.content = b"foo" - assert s._hash(r) == s._hash(r2) - r2.request.content = b"bar" - assert not s._hash(r) == s._hash(r2) - - # now ignoring content - s = flow.ServerPlaybackState( - None, - [], - False, - False, - None, - True, - None, - False) - r = tutils.tflow(resp=True) - r2 = tutils.tflow(resp=True) - r.request.content = b"foo" - r2.request.content = b"foo" - assert s._hash(r) == s._hash(r2) - r2.request.content = b"bar" - assert s._hash(r) == s._hash(r2) - r2.request.content = b"" - assert s._hash(r) == s._hash(r2) - r2.request.content = None - assert s._hash(r) == s._hash(r2) - - def test_ignore_host(self): - s = flow.ServerPlaybackState( - None, - [], - False, - False, - None, - False, - None, - True) - r = tutils.tflow(resp=True) - r2 = tutils.tflow(resp=True) - - r.request.host = "address" - r2.request.host = "address" - assert s._hash(r) == s._hash(r2) - r2.request.host = "wrong_address" - assert s._hash(r) == s._hash(r2) - - class TestHTTPFlow(object): def test_copy(self): @@ -699,13 +444,13 @@ class TestFlowMaster: fm = flow.FlowMaster(None, None, s) f = tutils.tflow(resp=True) f.request.content = None - assert "missing" in fm.replay_request(f) + tutils.raises("missing", fm.replay_request, f) f.intercepted = True - assert "intercepting" in fm.replay_request(f) + tutils.raises("intercepted", fm.replay_request, f) f.live = True - assert "live" in fm.replay_request(f) + tutils.raises("live", fm.replay_request, f) def test_duplicate_flow(self): s = flow.State() @@ -743,103 +488,6 @@ class TestFlowMaster: fm.shutdown() - def test_client_playback(self): - s = flow.State() - - f = tutils.tflow(resp=True) - pb = [tutils.tflow(resp=True), f] - fm = flow.FlowMaster( - options.Options(), - DummyServer(ProxyConfig(options.Options())), - s - ) - assert not fm.start_server_playback( - pb, - False, - [], - False, - False, - None, - False, - None, - False) - assert not fm.start_client_playback(pb, False) - fm.client_playback.testing = True - - assert not fm.state.flow_count() - fm.tick(0) - assert fm.state.flow_count() - - f.error = Error("error") - fm.error(f) - - def test_server_playback(self): - s = flow.State() - - f = tutils.tflow() - f.response = HTTPResponse.wrap(netlib.tutils.tresp(content=f.request)) - pb = [f] - - fm = flow.FlowMaster(options.Options(), None, s) - fm.refresh_server_playback = True - assert not fm.do_server_playback(tutils.tflow()) - - fm.start_server_playback( - pb, - False, - [], - False, - False, - None, - False, - None, - False) - assert fm.do_server_playback(tutils.tflow()) - - fm.start_server_playback( - pb, - False, - [], - True, - False, - None, - False, - None, - False) - r = tutils.tflow() - r.request.content = b"gibble" - assert not fm.do_server_playback(r) - assert fm.do_server_playback(tutils.tflow()) - - fm.tick(0) - assert fm.should_exit.is_set() - - fm.stop_server_playback() - assert not fm.server_playback - - def test_server_playback_kill(self): - s = flow.State() - f = tutils.tflow() - f.response = HTTPResponse.wrap(netlib.tutils.tresp(content=f.request)) - pb = [f] - fm = flow.FlowMaster(None, None, s) - fm.refresh_server_playback = True - fm.start_server_playback( - pb, - True, - [], - False, - False, - None, - False, - None, - False) - - f = tutils.tflow() - f.request.host = "nonexistent" - fm.request(f) - assert f.reply.value == Kill - class TestRequest: diff --git a/test/mitmproxy/test_fuzzing.py b/test/mitmproxy/test_fuzzing.py index 27ea36a6..905ba1cd 100644 --- a/test/mitmproxy/test_fuzzing.py +++ b/test/mitmproxy/test_fuzzing.py @@ -11,17 +11,20 @@ class TestFuzzy(tservers.HTTPProxyTest): def test_idna_err(self): req = r'get:"http://localhost:%s":i10,"\xc6"' p = self.pathoc() - assert p.request(req % self.server.port).status_code == 400 + with p.connect(): + assert p.request(req % self.server.port).status_code == 400 def test_nullbytes(self): req = r'get:"http://localhost:%s":i19,"\x00"' p = self.pathoc() - assert p.request(req % self.server.port).status_code == 400 + with p.connect(): + assert p.request(req % self.server.port).status_code == 400 def test_invalid_ipv6_url(self): req = 'get:"http://localhost:%s":i13,"["' p = self.pathoc() - resp = p.request(req % self.server.port) + with p.connect(): + resp = p.request(req % self.server.port) assert resp.status_code == 400 # def test_invalid_upstream(self): diff --git a/test/mitmproxy/test_server.py b/test/mitmproxy/test_server.py index e0a8da47..c5a5bb71 100644 --- a/test/mitmproxy/test_server.py +++ b/test/mitmproxy/test_server.py @@ -60,7 +60,7 @@ class CommonMixin: # Disconnect error l.request.path = "/p/305:d0" rt = self.master.replay_request(l, block=True) - assert not rt + assert rt if isinstance(self, tservers.HTTPUpstreamProxyTest): assert l.response.status_code == 502 else: @@ -72,7 +72,7 @@ class CommonMixin: # In upstream mode with ssl, the replay will fail as we cannot establish # SSL with the upstream proxy. rt = self.master.replay_request(l, block=True) - assert not rt + assert rt if isinstance(self, tservers.HTTPUpstreamProxyTest): assert l.response.status_code == 502 else: @@ -91,11 +91,11 @@ class CommonMixin: def test_invalid_http(self): t = tcp.TCPClient(("127.0.0.1", self.proxy.port)) - t.connect() - t.wfile.write(b"invalid\r\n\r\n") - t.wfile.flush() - line = t.rfile.readline() - assert (b"Bad Request" in line) or (b"Bad Gateway" in line) + with t.connect(): + t.wfile.write(b"invalid\r\n\r\n") + t.wfile.flush() + line = t.rfile.readline() + assert (b"Bad Request" in line) or (b"Bad Gateway" in line) def test_sni(self): if not self.ssl: @@ -208,20 +208,22 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin, AppMixin): def test_app_err(self): p = self.pathoc() - ret = p.request("get:'http://errapp/'") + with p.connect(): + ret = p.request("get:'http://errapp/'") assert ret.status_code == 500 assert b"ValueError" in ret.content def test_invalid_connect(self): t = tcp.TCPClient(("127.0.0.1", self.proxy.port)) - t.connect() - t.wfile.write(b"CONNECT invalid\n\n") - t.wfile.flush() - assert b"Bad Request" in t.rfile.readline() + with t.connect(): + t.wfile.write(b"CONNECT invalid\n\n") + t.wfile.flush() + assert b"Bad Request" in t.rfile.readline() def test_upstream_ssl_error(self): p = self.pathoc() - ret = p.request("get:'https://localhost:%s/'" % self.server.port) + with p.connect(): + ret = p.request("get:'https://localhost:%s/'" % self.server.port) assert ret.status_code == 400 def test_connection_close(self): @@ -232,25 +234,28 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin, AppMixin): # Lets sanity check that the connection does indeed stay open by # issuing two requests over the same connection p = self.pathoc() - assert p.request("get:'%s'" % response) - assert p.request("get:'%s'" % response) + with p.connect(): + assert p.request("get:'%s'" % response) + assert p.request("get:'%s'" % response) # Now check that the connection is closed as the client specifies p = self.pathoc() - assert p.request("get:'%s':h'Connection'='close'" % response) - # There's a race here, which means we can get any of a number of errors. - # Rather than introduce yet another sleep into the test suite, we just - # relax the Exception specification. - with raises(Exception): - p.request("get:'%s'" % response) + with p.connect(): + assert p.request("get:'%s':h'Connection'='close'" % response) + # There's a race here, which means we can get any of a number of errors. + # Rather than introduce yet another sleep into the test suite, we just + # relax the Exception specification. + with raises(Exception): + p.request("get:'%s'" % response) def test_reconnect(self): req = "get:'%s/p/200:b@1:da'" % self.server.urlbase p = self.pathoc() - assert p.request(req) - # Server has disconnected. Mitmproxy should detect this, and reconnect. - assert p.request(req) - assert p.request(req) + with p.connect(): + assert p.request(req) + # Server has disconnected. Mitmproxy should detect this, and reconnect. + assert p.request(req) + assert p.request(req) def test_get_connection_switching(self): def switched(l): @@ -260,18 +265,21 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin, AppMixin): req = "get:'%s/p/200:b@1'" p = self.pathoc() - assert p.request(req % self.server.urlbase) - assert p.request(req % self.server2.urlbase) + with p.connect(): + assert p.request(req % self.server.urlbase) + assert p.request(req % self.server2.urlbase) assert switched(self.proxy.tlog) def test_blank_leading_line(self): p = self.pathoc() - req = "get:'%s/p/201':i0,'\r\n'" - assert p.request(req % self.server.urlbase).status_code == 201 + with p.connect(): + req = "get:'%s/p/201':i0,'\r\n'" + assert p.request(req % self.server.urlbase).status_code == 201 def test_invalid_headers(self): p = self.pathoc() - resp = p.request("get:'http://foo':h':foo'='bar'") + with p.connect(): + resp = p.request("get:'http://foo':h':foo'='bar'") assert resp.status_code == 400 def test_stream(self): @@ -301,15 +309,16 @@ class TestHTTPAuth(tservers.HTTPProxyTest): self.master.options.auth_singleuser = "test:test" assert self.pathod("202").status_code == 407 p = self.pathoc() - ret = p.request(""" - get - 'http://localhost:%s/p/202' - h'%s'='%s' - """ % ( - self.server.port, - http.authentication.BasicProxyAuth.AUTH_HEADER, - authentication.assemble_http_basic_auth("basic", "test", "test") - )) + with p.connect(): + ret = p.request(""" + get + 'http://localhost:%s/p/202' + h'%s'='%s' + """ % ( + self.server.port, + http.authentication.BasicProxyAuth.AUTH_HEADER, + authentication.assemble_http_basic_auth("basic", "test", "test") + )) assert ret.status_code == 202 @@ -318,14 +327,15 @@ class TestHTTPReverseAuth(tservers.ReverseProxyTest): self.master.options.auth_singleuser = "test:test" assert self.pathod("202").status_code == 401 p = self.pathoc() - ret = p.request(""" - get - '/p/202' - h'%s'='%s' - """ % ( - http.authentication.BasicWebsiteAuth.AUTH_HEADER, - authentication.assemble_http_basic_auth("basic", "test", "test") - )) + with p.connect(): + ret = p.request(""" + get + '/p/202' + h'%s'='%s' + """ % ( + http.authentication.BasicWebsiteAuth.AUTH_HEADER, + authentication.assemble_http_basic_auth("basic", "test", "test") + )) assert ret.status_code == 202 @@ -354,7 +364,8 @@ class TestHTTPS(tservers.HTTPProxyTest, CommonMixin, TcpMixin): def test_error_post_connect(self): p = self.pathoc() - assert p.request("get:/:i0,'invalid\r\n\r\n'").status_code == 400 + with p.connect(): + assert p.request("get:/:i0,'invalid\r\n\r\n'").status_code == 400 class TestHTTPSCertfile(tservers.HTTPProxyTest, CommonMixin): @@ -389,7 +400,8 @@ class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxyTest): def _request(self): p = self.pathoc(sni="example.mitmproxy.org") - return p.request("get:/p/242") + with p.connect(): + return p.request("get:/p/242") def test_verification_w_cadir(self): self.config.options.update( @@ -426,7 +438,8 @@ class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxyTest): def _request(self): p = self.pathoc(sni="example.mitmproxy.org") - return p.request("get:/p/242") + with p.connect(): + return p.request("get:/p/242") @classmethod def get_options(cls): @@ -481,13 +494,15 @@ class TestSocks5(tservers.SocksModeTest): def test_simple(self): p = self.pathoc() - p.socks_connect(("localhost", self.server.port)) - f = p.request("get:/p/200") + with p.connect(): + p.socks_connect(("localhost", self.server.port)) + f = p.request("get:/p/200") assert f.status_code == 200 def test_with_authentication_only(self): p = self.pathoc() - f = p.request("get:/p/200") + with p.connect(): + f = p.request("get:/p/200") assert f.status_code == 502 assert b"SOCKS5 mode failure" in f.content @@ -496,21 +511,21 @@ class TestSocks5(tservers.SocksModeTest): mitmproxy doesn't support UDP or BIND SOCKS CMDs """ p = self.pathoc() - - socks.ClientGreeting( - socks.VERSION.SOCKS5, - [socks.METHOD.NO_AUTHENTICATION_REQUIRED] - ).to_file(p.wfile) - socks.Message( - socks.VERSION.SOCKS5, - socks.CMD.BIND, - socks.ATYP.DOMAINNAME, - ("example.com", 8080) - ).to_file(p.wfile) - - p.wfile.flush() - p.rfile.read(2) # read server greeting - f = p.request("get:/p/200") # the request doesn't matter, error response from handshake will be read anyway. + with p.connect(): + socks.ClientGreeting( + socks.VERSION.SOCKS5, + [socks.METHOD.NO_AUTHENTICATION_REQUIRED] + ).to_file(p.wfile) + socks.Message( + socks.VERSION.SOCKS5, + socks.CMD.BIND, + socks.ATYP.DOMAINNAME, + ("example.com", 8080) + ).to_file(p.wfile) + + p.wfile.flush() + p.rfile.read(2) # read server greeting + f = p.request("get:/p/200") # the request doesn't matter, error response from handshake will be read anyway. assert f.status_code == 502 assert b"SOCKS5 mode failure" in f.content @@ -531,21 +546,23 @@ class TestHttps2Http(tservers.ReverseProxyTest): p = pathoc.Pathoc( ("localhost", self.proxy.port), ssl=True, sni=sni, fp=None ) - p.connect() return p def test_all(self): p = self.pathoc(ssl=True) - assert p.request("get:'/p/200'").status_code == 200 + with p.connect(): + assert p.request("get:'/p/200'").status_code == 200 def test_sni(self): p = self.pathoc(ssl=True, sni="example.com") - assert p.request("get:'/p/200'").status_code == 200 - assert all("Error in handle_sni" not in msg for msg in self.proxy.tlog) + with p.connect(): + assert p.request("get:'/p/200'").status_code == 200 + assert all("Error in handle_sni" not in msg for msg in self.proxy.tlog) def test_http(self): p = self.pathoc(ssl=False) - assert p.request("get:'/p/200'").status_code == 200 + with p.connect(): + assert p.request("get:'/p/200'").status_code == 200 class TestTransparent(tservers.TransparentProxyTest, CommonMixin, TcpMixin): @@ -703,29 +720,29 @@ class TestRedirectRequest(tservers.HTTPProxyTest): self.master.redirect_port = self.server2.port p = self.pathoc() - - self.server.clear_log() - self.server2.clear_log() - r1 = p.request("get:'/p/200'") - assert r1.status_code == 200 - assert self.server.last_log() - assert not self.server2.last_log() - - self.server.clear_log() - self.server2.clear_log() - r2 = p.request("get:'/p/201'") - assert r2.status_code == 201 - assert not self.server.last_log() - assert self.server2.last_log() - - self.server.clear_log() - self.server2.clear_log() - r3 = p.request("get:'/p/202'") - assert r3.status_code == 202 - assert self.server.last_log() - assert not self.server2.last_log() - - assert r1.content == r2.content == r3.content + with p.connect(): + self.server.clear_log() + self.server2.clear_log() + r1 = p.request("get:'/p/200'") + assert r1.status_code == 200 + assert self.server.last_log() + assert not self.server2.last_log() + + self.server.clear_log() + self.server2.clear_log() + r2 = p.request("get:'/p/201'") + assert r2.status_code == 201 + assert not self.server.last_log() + assert self.server2.last_log() + + self.server.clear_log() + self.server2.clear_log() + r3 = p.request("get:'/p/202'") + assert r3.status_code == 202 + assert self.server.last_log() + assert not self.server2.last_log() + + assert r1.content == r2.content == r3.content class MasterStreamRequest(tservers.TestMaster): @@ -743,22 +760,22 @@ class TestStreamRequest(tservers.HTTPProxyTest): def test_stream_simple(self): p = self.pathoc() - - # a request with 100k of data but without content-length - r1 = p.request("get:'%s/p/200:r:b@100k:d102400'" % self.server.urlbase) - assert r1.status_code == 200 - assert len(r1.content) > 100000 + with p.connect(): + # a request with 100k of data but without content-length + r1 = p.request("get:'%s/p/200:r:b@100k:d102400'" % self.server.urlbase) + assert r1.status_code == 200 + assert len(r1.content) > 100000 def test_stream_multiple(self): p = self.pathoc() + with p.connect(): + # simple request with streaming turned on + r1 = p.request("get:'%s/p/200'" % self.server.urlbase) + assert r1.status_code == 200 - # simple request with streaming turned on - r1 = p.request("get:'%s/p/200'" % self.server.urlbase) - assert r1.status_code == 200 - - # now send back 100k of data, streamed but not chunked - r1 = p.request("get:'%s/p/201:b@100k'" % self.server.urlbase) - assert r1.status_code == 201 + # now send back 100k of data, streamed but not chunked + r1 = p.request("get:'%s/p/201:b@100k'" % self.server.urlbase) + assert r1.status_code == 201 def test_stream_chunked(self): connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -887,7 +904,8 @@ class TestUpstreamProxy(tservers.HTTPUpstreamProxyTest, CommonMixin, AppMixin): ("~s", "baz", "ORLY") ] p = self.pathoc() - req = p.request("get:'%s/p/418:b\"foo\"'" % self.server.urlbase) + with p.connect(): + req = p.request("get:'%s/p/418:b\"foo\"'" % self.server.urlbase) assert req.content == b"ORLY" assert req.status_code == 418 @@ -948,7 +966,8 @@ class TestUpstreamProxySSL( def test_simple(self): p = self.pathoc() - req = p.request("get:'/p/418:b\"content\"'") + with p.connect(): + req = p.request("get:'/p/418:b\"content\"'") assert req.content == b"content" assert req.status_code == 418 @@ -1006,48 +1025,49 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest): ]) p = self.pathoc() - req = p.request("get:'/p/418:b\"content\"'") - assert req.content == b"content" - assert req.status_code == 418 - - assert self.proxy.tmaster.state.flow_count() == 2 # CONNECT and request - # CONNECT, failing request, - assert self.chain[0].tmaster.state.flow_count() == 4 - # reCONNECT, request - # failing request, request - assert self.chain[1].tmaster.state.flow_count() == 2 - # (doesn't store (repeated) CONNECTs from chain[0] - # as it is a regular proxy) - - assert not self.chain[1].tmaster.state.flows[0].response # killed - assert self.chain[1].tmaster.state.flows[1].response - - assert self.proxy.tmaster.state.flows[0].request.first_line_format == "authority" - assert self.proxy.tmaster.state.flows[1].request.first_line_format == "relative" - - assert self.chain[0].tmaster.state.flows[ - 0].request.first_line_format == "authority" - assert self.chain[0].tmaster.state.flows[ - 1].request.first_line_format == "relative" - assert self.chain[0].tmaster.state.flows[ - 2].request.first_line_format == "authority" - assert self.chain[0].tmaster.state.flows[ - 3].request.first_line_format == "relative" - - assert self.chain[1].tmaster.state.flows[ - 0].request.first_line_format == "relative" - assert self.chain[1].tmaster.state.flows[ - 1].request.first_line_format == "relative" - - req = p.request("get:'/p/418:b\"content2\"'") - - assert req.status_code == 502 - assert self.proxy.tmaster.state.flow_count() == 3 # + new request - # + new request, repeated CONNECT from chain[1] - assert self.chain[0].tmaster.state.flow_count() == 6 - # (both terminated) - # nothing happened here - assert self.chain[1].tmaster.state.flow_count() == 2 + with p.connect(): + req = p.request("get:'/p/418:b\"content\"'") + assert req.content == b"content" + assert req.status_code == 418 + + assert self.proxy.tmaster.state.flow_count() == 2 # CONNECT and request + # CONNECT, failing request, + assert self.chain[0].tmaster.state.flow_count() == 4 + # reCONNECT, request + # failing request, request + assert self.chain[1].tmaster.state.flow_count() == 2 + # (doesn't store (repeated) CONNECTs from chain[0] + # as it is a regular proxy) + + assert not self.chain[1].tmaster.state.flows[0].response # killed + assert self.chain[1].tmaster.state.flows[1].response + + assert self.proxy.tmaster.state.flows[0].request.first_line_format == "authority" + assert self.proxy.tmaster.state.flows[1].request.first_line_format == "relative" + + assert self.chain[0].tmaster.state.flows[ + 0].request.first_line_format == "authority" + assert self.chain[0].tmaster.state.flows[ + 1].request.first_line_format == "relative" + assert self.chain[0].tmaster.state.flows[ + 2].request.first_line_format == "authority" + assert self.chain[0].tmaster.state.flows[ + 3].request.first_line_format == "relative" + + assert self.chain[1].tmaster.state.flows[ + 0].request.first_line_format == "relative" + assert self.chain[1].tmaster.state.flows[ + 1].request.first_line_format == "relative" + + req = p.request("get:'/p/418:b\"content2\"'") + + assert req.status_code == 502 + assert self.proxy.tmaster.state.flow_count() == 3 # + new request + # + new request, repeated CONNECT from chain[1] + assert self.chain[0].tmaster.state.flow_count() == 6 + # (both terminated) + # nothing happened here + assert self.chain[1].tmaster.state.flow_count() == 2 class AddUpstreamCertsToClientChainMixin: @@ -1066,12 +1086,13 @@ class AddUpstreamCertsToClientChainMixin: d = f.read() upstreamCert = SSLCert.from_pem(d) p = self.pathoc() - upstream_cert_found_in_client_chain = False - for receivedCert in p.server_certs: - if receivedCert.digest('sha256') == upstreamCert.digest('sha256'): - upstream_cert_found_in_client_chain = True - break - assert(upstream_cert_found_in_client_chain == self.master.options.add_upstream_certs_to_client_chain) + with p.connect(): + upstream_cert_found_in_client_chain = False + for receivedCert in p.server_certs: + if receivedCert.digest('sha256') == upstreamCert.digest('sha256'): + upstream_cert_found_in_client_chain = True + break + assert(upstream_cert_found_in_client_chain == self.master.options.add_upstream_certs_to_client_chain) class TestHTTPSAddUpstreamCertsToClientChainTrue( diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py index 1597f59c..4291f743 100644 --- a/test/mitmproxy/tservers.py +++ b/test/mitmproxy/tservers.py @@ -3,6 +3,7 @@ import threading import tempfile import flask import mock +import sys from mitmproxy.proxy.config import ProxyConfig from mitmproxy.proxy.server import ProxyServer @@ -10,6 +11,7 @@ import pathod.test import pathod.pathoc from mitmproxy import flow, controller, options from mitmproxy import builtins +import netlib.exceptions testapp = flask.Flask(__name__) @@ -104,6 +106,14 @@ class ProxyTestBase(object): cls.server.shutdown() cls.server2.shutdown() + def teardown(self): + try: + self.server.wait_for_silence() + except netlib.exceptions.Timeout: + # FIXME: Track down the Windows sync issues + if sys.platform != "win32": + raise + def setup(self): self.master.clear_log() self.master.state.clear() @@ -125,6 +135,15 @@ class ProxyTestBase(object): ) +class LazyPathoc(pathod.pathoc.Pathoc): + def __init__(self, lazy_connect, *args, **kwargs): + self.lazy_connect = lazy_connect + pathod.pathoc.Pathoc.__init__(self, *args, **kwargs) + + def connect(self): + return pathod.pathoc.Pathoc.connect(self, self.lazy_connect) + + class HTTPProxyTest(ProxyTestBase): def pathoc_raw(self): @@ -134,14 +153,14 @@ class HTTPProxyTest(ProxyTestBase): """ Returns a connected Pathoc instance. """ - p = pathod.pathoc.Pathoc( - ("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None - ) if self.ssl: - p.connect(("127.0.0.1", self.server.port)) + conn = ("127.0.0.1", self.server.port) else: - p.connect() - return p + conn = None + return LazyPathoc( + conn, + ("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None + ) def pathod(self, spec, sni=None): """ @@ -152,18 +171,20 @@ class HTTPProxyTest(ProxyTestBase): q = "get:'/p/%s'" % spec else: q = "get:'%s/p/%s'" % (self.server.urlbase, spec) - return p.request(q) + with p.connect(): + return p.request(q) def app(self, page): if self.ssl: p = pathod.pathoc.Pathoc( ("127.0.0.1", self.proxy.port), True, fp=None ) - p.connect((options.APP_HOST, options.APP_PORT)) - return p.request("get:'%s'" % page) + with p.connect((options.APP_HOST, options.APP_PORT)): + return p.request("get:'%s'" % page) else: p = self.pathoc() - return p.request("get:'http://%s%s'" % (options.APP_HOST, page)) + with p.connect(): + return p.request("get:'http://%s%s'" % (options.APP_HOST, page)) class TResolver: @@ -210,7 +231,8 @@ class TransparentProxyTest(ProxyTestBase): else: p = self.pathoc() q = "get:'/p/%s'" % spec - return p.request(q) + with p.connect(): + return p.request(q) def pathoc(self, sni=None): """ @@ -219,7 +241,6 @@ class TransparentProxyTest(ProxyTestBase): p = pathod.pathoc.Pathoc( ("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None ) - p.connect() return p @@ -247,7 +268,6 @@ class ReverseProxyTest(ProxyTestBase): p = pathod.pathoc.Pathoc( ("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None ) - p.connect() return p def pathod(self, spec, sni=None): @@ -260,7 +280,8 @@ class ReverseProxyTest(ProxyTestBase): else: p = self.pathoc() q = "get:'/p/%s'" % spec - return p.request(q) + with p.connect(): + return p.request(q) class SocksModeTest(HTTPProxyTest): diff --git a/test/netlib/http/test_headers.py b/test/netlib/http/test_headers.py index ad2bc548..e8752c52 100644 --- a/test/netlib/http/test_headers.py +++ b/test/netlib/http/test_headers.py @@ -43,6 +43,15 @@ class TestHeaders(object): with raises(TypeError): Headers([[b"Host", u"not-bytes"]]) + def test_set(self): + headers = Headers() + headers[u"foo"] = u"1" + headers[b"bar"] = b"2" + headers["baz"] = b"3" + with raises(TypeError): + headers["foobar"] = 42 + assert len(headers) == 3 + def test_bytes(self): headers = Headers(Host="example.com") assert bytes(headers) == b"Host: example.com\r\n" diff --git a/test/netlib/http/test_response.py b/test/netlib/http/test_response.py index c7b1b646..e97cc419 100644 --- a/test/netlib/http/test_response.py +++ b/test/netlib/http/test_response.py @@ -34,6 +34,11 @@ class TestResponseCore(object): assert r.status_code == 200 assert r.content == b"" + r = Response.make(418, "teatime") + assert r.status_code == 418 + assert r.content == b"teatime" + assert r.headers["content-length"] == "7" + Response.make(content=b"foo") Response.make(content="foo") with raises(TypeError): diff --git a/test/netlib/test_encoding.py b/test/netlib/test_encoding.py index 08e69ec5..e1175ef0 100644 --- a/test/netlib/test_encoding.py +++ b/test/netlib/test_encoding.py @@ -1,55 +1,46 @@ import mock +import pytest + from netlib import encoding, tutils -def test_identity(): - assert b"string" == encoding.decode(b"string", "identity") - assert b"string" == encoding.encode(b"string", "identity") +@pytest.mark.parametrize("encoder", [ + 'identity', + 'none', +]) +def test_identity(encoder): + assert b"string" == encoding.decode(b"string", encoder) + assert b"string" == encoding.encode(b"string", encoder) with tutils.raises(ValueError): encoding.encode(b"string", "nonexistent encoding") -def test_gzip(): - assert b"string" == encoding.decode( - encoding.encode( - b"string", - "gzip" - ), - "gzip" - ) - with tutils.raises(ValueError): - encoding.decode(b"bogus", "gzip") +@pytest.mark.parametrize("encoder", [ + 'gzip', + 'br', + 'deflate', +]) +def test_encoders(encoder): + assert "" == encoding.decode("", encoder) + assert b"" == encoding.decode(b"", encoder) - -def test_brotli(): - assert b"string" == encoding.decode( + assert "string" == encoding.decode( encoding.encode( - b"string", - "br" + "string", + encoder ), - "br" + encoder ) - with tutils.raises(ValueError): - encoding.decode(b"bogus", "br") - - -def test_deflate(): assert b"string" == encoding.decode( encoding.encode( b"string", - "deflate" + encoder ), - "deflate" - ) - assert b"string" == encoding.decode( - encoding.encode( - b"string", - "deflate" - )[2:-4], - "deflate" + encoder ) + with tutils.raises(ValueError): - encoding.decode(b"bogus", "deflate") + encoding.decode(b"foobar", encoder) def test_cache(): diff --git a/test/netlib/test_strutils.py b/test/netlib/test_strutils.py index 5be254a3..0f58cac5 100644 --- a/test/netlib/test_strutils.py +++ b/test/netlib/test_strutils.py @@ -8,6 +8,8 @@ def test_always_bytes(): assert strutils.always_bytes("foo") == b"foo" with tutils.raises(ValueError): strutils.always_bytes(u"\u2605", "ascii") + with tutils.raises(TypeError): + strutils.always_bytes(42, "ascii") def test_native(): diff --git a/test/pathod/test_protocols_http2.py b/test/pathod/test_protocols_http2.py index 8d7efc82..7f65c0eb 100644 --- a/test/pathod/test_protocols_http2.py +++ b/test/pathod/test_protocols_http2.py @@ -5,7 +5,7 @@ import hyperframe from netlib import tcp, http from netlib.tutils import raises from netlib.exceptions import TcpDisconnect -from netlib.http.http2 import framereader +from netlib.http import http2 from ..netlib import tservers as netlib_tservers @@ -112,11 +112,11 @@ class TestPerformServerConnectionPreface(netlib_tservers.ServerTestBase): self.wfile.flush() # check empty settings frame - raw = framereader.http2_read_raw_frame(self.rfile) + raw = http2.read_raw_frame(self.rfile) assert raw == codecs.decode('00000c040000000000000200000000000300000001', 'hex_codec') # check settings acknowledgement - raw = framereader.http2_read_raw_frame(self.rfile) + raw = http2.read_raw_frame(self.rfile) assert raw == codecs.decode('000000040100000000', 'hex_codec') # send settings acknowledgement |