aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/.gitignore1
-rw-r--r--test/.pry6
-rw-r--r--test/test_dump.py6
-rw-r--r--test/test_flow.py32
-rw-r--r--test/test_proxy.py11
-rw-r--r--test/test_server.py132
-rw-r--r--test/tservers.py198
-rw-r--r--test/tutils.py176
8 files changed, 363 insertions, 199 deletions
diff --git a/test/.gitignore b/test/.gitignore
deleted file mode 100644
index 6350e986..00000000
--- a/test/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.coverage
diff --git a/test/.pry b/test/.pry
deleted file mode 100644
index f6f18e7b..00000000
--- a/test/.pry
+++ /dev/null
@@ -1,6 +0,0 @@
-base = ..
-coverage = ../libmproxy
-exclude = .
- ../libmproxy/contrib
- ../libmproxy/tnetstring.py
-
diff --git a/test/test_dump.py b/test/test_dump.py
index e1241e29..5d3f9133 100644
--- a/test/test_dump.py
+++ b/test/test_dump.py
@@ -3,6 +3,7 @@ from cStringIO import StringIO
import libpry
from libmproxy import dump, flow, proxy
import tutils
+import mock
def test_strfuncs():
t = tutils.tresp()
@@ -21,6 +22,7 @@ class TestDumpMaster:
req = tutils.treq()
req.content = content
l = proxy.Log("connect")
+ l.reply = mock.MagicMock()
m.handle_log(l)
cc = req.client_conn
cc.connection_error = "error"
@@ -29,7 +31,9 @@ class TestDumpMaster:
m.handle_clientconnect(cc)
m.handle_request(req)
f = m.handle_response(resp)
- m.handle_clientdisconnect(flow.ClientDisconnect(cc))
+ cd = flow.ClientDisconnect(cc)
+ cd.reply = mock.MagicMock()
+ m.handle_clientdisconnect(cd)
return f
def _dummy_cycle(self, n, filt, content, **options):
diff --git a/test/test_flow.py b/test/test_flow.py
index da5b095e..6aa898ad 100644
--- a/test/test_flow.py
+++ b/test/test_flow.py
@@ -223,16 +223,16 @@ class TestFlow:
f = tutils.tflow()
f.request = tutils.treq()
f.intercept()
- assert not f.request.acked
+ assert not f.request.reply.acked
f.kill(fm)
- assert f.request.acked
+ assert f.request.reply.acked
f.intercept()
f.response = tutils.tresp()
f.request = f.response.request
- f.request._ack()
- assert not f.response.acked
+ f.request.reply()
+ assert not f.response.reply.acked
f.kill(fm)
- assert f.response.acked
+ assert f.response.reply.acked
def test_killall(self):
s = flow.State()
@@ -245,25 +245,25 @@ class TestFlow:
fm.handle_request(r)
for i in s.view:
- assert not i.request.acked
+ assert not i.request.reply.acked
s.killall(fm)
for i in s.view:
- assert i.request.acked
+ assert i.request.reply.acked
def test_accept_intercept(self):
f = tutils.tflow()
f.request = tutils.treq()
f.intercept()
- assert not f.request.acked
+ assert not f.request.reply.acked
f.accept_intercept()
- assert f.request.acked
+ assert f.request.reply.acked
f.response = tutils.tresp()
f.request = f.response.request
f.intercept()
- f.request._ack()
- assert not f.response.acked
+ f.request.reply()
+ assert not f.response.reply.acked
f.accept_intercept()
- assert f.response.acked
+ assert f.response.reply.acked
def test_serialization(self):
f = flow.Flow(None)
@@ -562,9 +562,11 @@ class TestFlowMaster:
fm.handle_response(resp)
assert fm.script.ns["log"][-1] == "response"
dc = flow.ClientDisconnect(req.client_conn)
+ dc.reply = controller.DummyReply()
fm.handle_clientdisconnect(dc)
assert fm.script.ns["log"][-1] == "clientdisconnect"
err = flow.Error(f.request, "msg")
+ err.reply = controller.DummyReply()
fm.handle_error(err)
assert fm.script.ns["log"][-1] == "error"
@@ -598,10 +600,12 @@ class TestFlowMaster:
assert not fm.handle_response(rx)
dc = flow.ClientDisconnect(req.client_conn)
+ dc.reply = controller.DummyReply()
req.client_conn.requestcount = 1
fm.handle_clientdisconnect(dc)
err = flow.Error(f.request, "msg")
+ err.reply = controller.DummyReply()
fm.handle_error(err)
fm.load_script(tutils.test_data.path("scripts/a.py"))
@@ -621,7 +625,9 @@ class TestFlowMaster:
fm.tick(q)
assert fm.state.flow_count()
- fm.handle_error(flow.Error(f.request, "error"))
+ err = flow.Error(f.request, "error")
+ err.reply = controller.DummyReply()
+ fm.handle_error(err)
def test_server_playback(self):
controller.should_exit = False
diff --git a/test/test_proxy.py b/test/test_proxy.py
index c73f61d8..3995b393 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -1,7 +1,7 @@
from libmproxy import proxy, flow
import tutils
from libpathod import test
-from netlib import http
+from netlib import http, tcp
import mock
@@ -39,8 +39,8 @@ class TestServerConnection:
self.d.shutdown()
def test_simple(self):
- sc = proxy.ServerConnection(proxy.ProxyConfig(), self.d.IFACE, self.d.port)
- sc.connect("http")
+ sc = proxy.ServerConnection(proxy.ProxyConfig(), "http", self.d.IFACE, self.d.port, "host.com")
+ sc.connect()
r = tutils.treq()
r.path = "/p/200:da"
sc.send(r)
@@ -53,8 +53,9 @@ class TestServerConnection:
sc.terminate()
def test_terminate_error(self):
- sc = proxy.ServerConnection(proxy.ProxyConfig(), self.d.IFACE, self.d.port)
- sc.connect("http")
+ sc = proxy.ServerConnection(proxy.ProxyConfig(), "http", self.d.IFACE, self.d.port, "host.com")
+ sc.connect()
sc.connection = mock.Mock()
sc.connection.close = mock.Mock(side_effect=IOError)
sc.terminate()
+
diff --git a/test/test_server.py b/test/test_server.py
index 0a2f142e..034fab41 100644
--- a/test/test_server.py
+++ b/test/test_server.py
@@ -1,7 +1,9 @@
import socket, time
+import mock
from netlib import tcp
from libpathod import pathoc
-import tutils
+import tutils, tservers
+from libmproxy import flow, proxy
"""
Note that the choice of response code in these tests matters more than you
@@ -39,7 +41,19 @@ class SanityMixin:
assert l.error
-class TestHTTP(tutils.HTTPProxTest, SanityMixin):
+class TestHTTP(tservers.HTTPProxTest, SanityMixin):
+ def test_app(self):
+ p = self.pathoc()
+ ret = p.request("get:'http://testapp/'")
+ assert ret[1] == 200
+ assert ret[4] == "testapp"
+
+ def test_app_err(self):
+ p = self.pathoc()
+ ret = p.request("get:'http://errapp/'")
+ assert ret[1] == 500
+ assert "ValueError" in ret[4]
+
def test_invalid_http(self):
t = tcp.TCPClient("127.0.0.1", self.proxy.port)
t.connect()
@@ -68,24 +82,83 @@ class TestHTTP(tutils.HTTPProxTest, SanityMixin):
assert "host" in l.request.headers
assert l.response.code == 304
+ def test_connection_close(self):
+ # Add a body, so we have a content-length header, which combined with
+ # HTTP1.1 means the connection is kept alive.
+ response = '%s/p/200:b@1'%self.server.urlbase
+
+ # 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)
+
+ # Now check that the connection is closed as the client specifies
+ p = self.pathoc()
+ assert p.request("get:'%s':h'Connection'='close'"%response)
+ tutils.raises("disconnect", 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)
-class TestHTTPS(tutils.HTTPProxTest, SanityMixin):
+ # However, if the server disconnects on our first try, it's an error.
+ req = "get:'%s/p/200:b@1:d0'"%self.server.urlbase
+ p = self.pathoc()
+ tutils.raises("server disconnect", p.request, req)
+
+ def test_proxy_ioerror(self):
+ # Tests a difficult-to-trigger condition, where an IOError is raised
+ # within our read loop.
+ with mock.patch("libmproxy.proxy.ProxyHandler.read_request") as m:
+ m.side_effect = IOError("error!")
+ tutils.raises("empty reply", self.pathod, "304")
+
+ def test_get_connection_switching(self):
+ def switched(l):
+ for i in l:
+ if "switching" in i:
+ return True
+ req = "get:'%s/p/200:b@1'"
+ p = self.pathoc()
+ assert p.request(req%self.server.urlbase)
+ assert p.request(req%self.server2.urlbase)
+ assert switched(self.proxy.log)
+
+ def test_get_connection_err(self):
+ p = self.pathoc()
+ ret = p.request("get:'http://localhost:0'")
+ assert ret[1] == 502
+
+
+class TestHTTPS(tservers.HTTPProxTest, SanityMixin):
ssl = True
clientcerts = True
def test_clientcert(self):
f = self.pathod("304")
- assert self.last_log()["request"]["clientcert"]["keyinfo"]
+ assert self.server.last_log()["request"]["clientcert"]["keyinfo"]
+
+
+class TestHTTPSCertfile(tservers.HTTPProxTest, SanityMixin):
+ ssl = True
+ certfile = True
+ def test_certfile(self):
+ assert self.pathod("304")
-class TestReverse(tutils.ReverseProxTest, SanityMixin):
+class TestReverse(tservers.ReverseProxTest, SanityMixin):
reverse = True
-class TestTransparent(tutils.TransparentProxTest, SanityMixin):
+class TestTransparent(tservers.TransparentProxTest, SanityMixin):
transparent = True
-class TestProxy(tutils.HTTPProxTest):
+class TestProxy(tservers.HTTPProxTest):
def test_http(self):
f = self.pathod("304")
assert f.status_code == 304
@@ -132,3 +205,48 @@ class TestProxy(tutils.HTTPProxTest):
request = self.master.state.view[1].request
assert request.timestamp_end - request.timestamp_start <= 0.1
+
+
+
+class MasterFakeResponse(tservers.TestMaster):
+ def handle_request(self, m):
+ resp = tutils.tresp()
+ m.reply(resp)
+
+
+class TestFakeResponse(tservers.HTTPProxTest):
+ masterclass = MasterFakeResponse
+ def test_kill(self):
+ p = self.pathoc()
+ f = self.pathod("200")
+ assert "header_response" in f.headers.keys()
+
+
+
+class MasterKillRequest(tservers.TestMaster):
+ def handle_request(self, m):
+ m.reply(proxy.KILL)
+
+
+class TestKillRequest(tservers.HTTPProxTest):
+ masterclass = MasterKillRequest
+ def test_kill(self):
+ p = self.pathoc()
+ tutils.raises("empty reply", self.pathod, "200")
+ # Nothing should have hit the server
+ assert not self.server.last_log()
+
+
+class MasterKillResponse(tservers.TestMaster):
+ def handle_response(self, m):
+ m.reply(proxy.KILL)
+
+
+class TestKillResponse(tservers.HTTPProxTest):
+ masterclass = MasterKillResponse
+ def test_kill(self):
+ p = self.pathoc()
+ tutils.raises("empty reply", self.pathod, "200")
+ # The server should have seen a request
+ assert self.server.last_log()
+
diff --git a/test/tservers.py b/test/tservers.py
new file mode 100644
index 00000000..998ad6c6
--- /dev/null
+++ b/test/tservers.py
@@ -0,0 +1,198 @@
+import threading, Queue
+import flask
+import human_curl as hurl
+import libpathod.test, libpathod.pathoc
+from libmproxy import proxy, flow, controller
+import tutils
+
+testapp = flask.Flask(__name__)
+
+@testapp.route("/")
+def hello():
+ return "testapp"
+
+@testapp.route("/error")
+def error():
+ raise ValueError("An exception...")
+
+
+def errapp(environ, start_response):
+ raise ValueError("errapp")
+
+
+class TestMaster(flow.FlowMaster):
+ def __init__(self, testq, config):
+ s = proxy.ProxyServer(config, 0)
+ s.apps.add(testapp, "testapp", 80)
+ s.apps.add(errapp, "errapp", 80)
+ state = flow.State()
+ flow.FlowMaster.__init__(self, s, state)
+ self.testq = testq
+ self.log = []
+
+ def handle_request(self, m):
+ flow.FlowMaster.handle_request(self, m)
+ m.reply()
+
+ def handle_response(self, m):
+ flow.FlowMaster.handle_response(self, m)
+ m.reply()
+
+ def handle_log(self, l):
+ self.log.append(l.msg)
+ l.reply()
+
+
+class ProxyThread(threading.Thread):
+ def __init__(self, tmaster):
+ threading.Thread.__init__(self)
+ self.tmaster = tmaster
+ controller.should_exit = False
+
+ @property
+ def port(self):
+ return self.tmaster.server.port
+
+ @property
+ def log(self):
+ return self.tmaster.log
+
+ def run(self):
+ self.tmaster.run()
+
+ def shutdown(self):
+ self.tmaster.shutdown()
+
+
+class ProxTestBase:
+ # Test Configuration
+ ssl = None
+ clientcerts = False
+ certfile = None
+
+ masterclass = TestMaster
+ @classmethod
+ def setupAll(cls):
+ cls.tqueue = Queue.Queue()
+ cls.server = libpathod.test.Daemon(ssl=cls.ssl)
+ cls.server2 = libpathod.test.Daemon(ssl=cls.ssl)
+ pconf = cls.get_proxy_config()
+ config = proxy.ProxyConfig(
+ cacert = tutils.test_data.path("data/serverkey.pem"),
+ **pconf
+ )
+ tmaster = cls.masterclass(cls.tqueue, config)
+ cls.proxy = ProxyThread(tmaster)
+ cls.proxy.start()
+
+ @property
+ def master(cls):
+ return cls.proxy.tmaster
+
+ @classmethod
+ def teardownAll(cls):
+ cls.proxy.shutdown()
+ cls.server.shutdown()
+ cls.server2.shutdown()
+
+ def setUp(self):
+ self.master.state.clear()
+
+ @property
+ def scheme(self):
+ return "https" if self.ssl else "http"
+
+ @property
+ def proxies(self):
+ """
+ The URL base for the server instance.
+ """
+ return (
+ (self.scheme, ("127.0.0.1", self.proxy.port))
+ )
+
+ @classmethod
+ def get_proxy_config(cls):
+ d = dict()
+ if cls.clientcerts:
+ d["clientcerts"] = tutils.test_data.path("data/clientcert")
+ if cls.certfile:
+ d["certfile"] =tutils.test_data.path("data/testkey.pem")
+ return d
+
+
+class HTTPProxTest(ProxTestBase):
+ def pathoc(self, connect_to = None):
+ """
+ Returns a connected Pathoc instance.
+ """
+ p = libpathod.pathoc.Pathoc("localhost", self.proxy.port)
+ p.connect(connect_to)
+ return p
+
+ def pathod(self, spec):
+ """
+ Constructs a pathod request, with the appropriate base and proxy.
+ """
+ return hurl.get(
+ self.server.urlbase + "/p/" + spec,
+ proxy=self.proxies,
+ validate_cert=False,
+ #debug=hurl.utils.stdout_debug
+ )
+
+
+class TResolver:
+ def __init__(self, port):
+ self.port = port
+
+ def original_addr(self, sock):
+ return ("127.0.0.1", self.port)
+
+
+class TransparentProxTest(ProxTestBase):
+ ssl = None
+ @classmethod
+ def get_proxy_config(cls):
+ d = ProxTestBase.get_proxy_config()
+ d["transparent_proxy"] = dict(
+ resolver = TResolver(cls.server.port),
+ sslports = []
+ )
+ return d
+
+ def pathod(self, spec):
+ """
+ Constructs a pathod request, with the appropriate base and proxy.
+ """
+ r = hurl.get(
+ "http://127.0.0.1:%s"%self.proxy.port + "/p/" + spec,
+ validate_cert=False,
+ #debug=hurl.utils.stdout_debug
+ )
+ return r
+
+
+class ReverseProxTest(ProxTestBase):
+ ssl = None
+ @classmethod
+ def get_proxy_config(cls):
+ d = ProxTestBase.get_proxy_config()
+ d["reverse_proxy"] = (
+ "https" if cls.ssl else "http",
+ "127.0.0.1",
+ cls.server.port
+ )
+ return d
+
+ def pathod(self, spec):
+ """
+ Constructs a pathod request, with the appropriate base and proxy.
+ """
+ r = hurl.get(
+ "http://127.0.0.1:%s"%self.proxy.port + "/p/" + spec,
+ validate_cert=False,
+ #debug=hurl.utils.stdout_debug
+ )
+ return r
+
diff --git a/test/tutils.py b/test/tutils.py
index 9868c778..1a1c8724 100644
--- a/test/tutils.py
+++ b/test/tutils.py
@@ -1,17 +1,18 @@
-import threading, Queue
import os, shutil, tempfile
from contextlib import contextmanager
-from libmproxy import proxy, flow, controller, utils
+from libmproxy import flow, utils, controller
from netlib import certutils
-import human_curl as hurl
-import libpathod.test, libpathod.pathoc
+import mock
def treq(conn=None):
if not conn:
conn = flow.ClientConnect(("address", 22))
+ conn.reply = controller.DummyReply()
headers = flow.ODictCaseless()
headers["header"] = ["qvalue"]
- return flow.Request(conn, (1, 1), "host", 80, "http", "GET", "/path", headers, "content")
+ r = flow.Request(conn, (1, 1), "host", 80, "http", "GET", "/path", headers, "content")
+ r.reply = controller.DummyReply()
+ return r
def tresp(req=None):
@@ -20,7 +21,9 @@ def tresp(req=None):
headers = flow.ODictCaseless()
headers["header_response"] = ["svalue"]
cert = certutils.SSLCert.from_der(file(test_data.path("data/dercert")).read())
- return flow.Response(req, (1, 1), 200, "message", headers, "content_response", cert)
+ resp = flow.Response(req, (1, 1), 200, "message", headers, "content_response", cert)
+ resp.reply = controller.DummyReply()
+ return resp
def tflow():
@@ -39,168 +42,10 @@ def tflow_err():
r = treq()
f = flow.Flow(r)
f.error = flow.Error(r, "error")
+ f.error.reply = controller.DummyReply()
return f
-class TestMaster(flow.FlowMaster):
- def __init__(self, testq, config):
- s = proxy.ProxyServer(config, 0)
- state = flow.State()
- flow.FlowMaster.__init__(self, s, state)
- self.testq = testq
-
- def handle(self, m):
- flow.FlowMaster.handle(self, m)
- m._ack()
-
-
-class ProxyThread(threading.Thread):
- def __init__(self, testq, config):
- self.tmaster = TestMaster(testq, config)
- controller.should_exit = False
- threading.Thread.__init__(self)
-
- @property
- def port(self):
- return self.tmaster.server.port
-
- def run(self):
- self.tmaster.run()
-
- def shutdown(self):
- self.tmaster.shutdown()
-
-
-class ProxTestBase:
- @classmethod
- def setupAll(cls):
- cls.tqueue = Queue.Queue()
- cls.server = libpathod.test.Daemon(ssl=cls.ssl)
- pconf = cls.get_proxy_config()
- config = proxy.ProxyConfig(
- certfile=test_data.path("data/testkey.pem"),
- **pconf
- )
- cls.proxy = ProxyThread(cls.tqueue, config)
- cls.proxy.start()
-
- @property
- def master(cls):
- return cls.proxy.tmaster
-
- @classmethod
- def teardownAll(cls):
- cls.proxy.shutdown()
- cls.server.shutdown()
-
- def setUp(self):
- self.master.state.clear()
-
- @property
- def scheme(self):
- return "https" if self.ssl else "http"
-
- @property
- def proxies(self):
- """
- The URL base for the server instance.
- """
- return (
- (self.scheme, ("127.0.0.1", self.proxy.port))
- )
-
- @property
- def urlbase(self):
- """
- The URL base for the server instance.
- """
- return self.server.urlbase
-
- def last_log(self):
- return self.server.last_log()
-
-
-class HTTPProxTest(ProxTestBase):
- ssl = None
- clientcerts = False
- @classmethod
- def get_proxy_config(cls):
- d = dict()
- if cls.clientcerts:
- d["clientcerts"] = test_data.path("data/clientcert")
- return d
-
- def pathoc(self, connect_to = None):
- p = libpathod.pathoc.Pathoc("localhost", self.proxy.port)
- p.connect(connect_to)
- return p
-
- def pathod(self, spec):
- """
- Constructs a pathod request, with the appropriate base and proxy.
- """
- return hurl.get(
- self.urlbase + "/p/" + spec,
- proxy=self.proxies,
- validate_cert=False,
- #debug=hurl.utils.stdout_debug
- )
-
-
-class TResolver:
- def __init__(self, port):
- self.port = port
-
- def original_addr(self, sock):
- return ("127.0.0.1", self.port)
-
-
-class TransparentProxTest(ProxTestBase):
- ssl = None
- @classmethod
- def get_proxy_config(cls):
- return dict(
- transparent_proxy = dict(
- resolver = TResolver(cls.server.port),
- sslports = []
- )
- )
-
- def pathod(self, spec):
- """
- Constructs a pathod request, with the appropriate base and proxy.
- """
- r = hurl.get(
- "http://127.0.0.1:%s"%self.proxy.port + "/p/" + spec,
- validate_cert=False,
- #debug=hurl.utils.stdout_debug
- )
- return r
-
-
-class ReverseProxTest(ProxTestBase):
- ssl = None
- @classmethod
- def get_proxy_config(cls):
- return dict(
- reverse_proxy = (
- "https" if cls.ssl else "http",
- "127.0.0.1",
- cls.server.port
- )
- )
-
- def pathod(self, spec):
- """
- Constructs a pathod request, with the appropriate base and proxy.
- """
- r = hurl.get(
- "http://127.0.0.1:%s"%self.proxy.port + "/p/" + spec,
- validate_cert=False,
- #debug=hurl.utils.stdout_debug
- )
- return r
-
@contextmanager
def tmpdir(*args, **kwargs):
@@ -252,5 +97,4 @@ def raises(exc, obj, *args, **kwargs):
)
raise AssertionError("No exception raised.")
-
test_data = utils.Data(__name__)