mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 00:01:36 +00:00
Test WSGI app calling.
- Factor out test servers into a separate file - Adjust docs to note new Flask dependency
This commit is contained in:
parent
782bbee8c0
commit
1ccb2c5dea
28
README.mkd
28
README.mkd
@ -50,28 +50,24 @@ Requirements
|
||||
------------
|
||||
|
||||
* [Python](http://www.python.org) 2.7.x.
|
||||
* [netlib](http://pypi.python.org/pypi/netlib) 0.2.2 or newer.
|
||||
* [PyOpenSSL](http://pypi.python.org/pypi/pyOpenSSL) 0.13 or newer.
|
||||
* [pyasn1](http://pypi.python.org/pypi/pyasn1) 0.1.2 or newer.
|
||||
* [urwid](http://excess.org/urwid/) version 1.1 or newer.
|
||||
* [PIL](http://www.pythonware.com/products/pil/) version 1.1 or newer.
|
||||
* [lxml](http://lxml.de/) version 2.3 or newer.
|
||||
* [netlib](http://pypi.python.org/pypi/netlib) 0.2.2 or newer.
|
||||
|
||||
The following auxiliary components may be needed if you plan to hack on
|
||||
mitmproxy:
|
||||
|
||||
* The test suite uses the [nose](http://readthedocs.org/docs/nose/en/latest/) unit testing
|
||||
framework and requires [human_curl](https://github.com/Lispython/human_curl) and
|
||||
[pathod](http://pathod.org).
|
||||
* Rendering the documentation requires [countershape](http://github.com/cortesi/countershape).
|
||||
|
||||
__mitmproxy__ is tested and developed on OSX, Linux and OpenBSD. Windows is not
|
||||
supported at the moment.
|
||||
officially supported at the moment.
|
||||
|
||||
You should also make sure that your console environment is set up with the
|
||||
following:
|
||||
|
||||
* EDITOR environment variable to determine the external editor.
|
||||
* PAGER environment variable to determine the external pager.
|
||||
* Appropriate entries in your mailcap files to determine external
|
||||
viewers for request and response contents.
|
||||
Hacking
|
||||
-------
|
||||
|
||||
The following components are needed if you plan to hack on mitmproxy:
|
||||
|
||||
* The test suite uses the [nose](http://readthedocs.org/docs/nose/en/latest/) unit testing
|
||||
framework and requires [human_curl](https://github.com/Lispython/human_curl),
|
||||
[pathod](http://pathod.org) and [flask](http://flask.pocoo.org/).
|
||||
* Rendering the documentation requires [countershape](http://github.com/cortesi/countershape).
|
||||
|
||||
|
@ -67,7 +67,7 @@ def _dummysc(config, host, port):
|
||||
|
||||
def _errsc(config, host, port):
|
||||
m = mock.MagicMock(config=config, host=host, port=port)
|
||||
m.connect = mock.MagicMock(side_effect=tcp.NetLibError())
|
||||
m.connect = mock.MagicMock(side_effect=tcp.NetLibError())
|
||||
return m
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import socket, time
|
||||
from netlib import tcp
|
||||
from libpathod import pathoc
|
||||
import tutils
|
||||
import tutils, tservers
|
||||
|
||||
"""
|
||||
Note that the choice of response code in these tests matters more than you
|
||||
@ -39,7 +39,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()
|
||||
@ -69,7 +81,7 @@ class TestHTTP(tutils.HTTPProxTest, SanityMixin):
|
||||
assert l.response.code == 304
|
||||
|
||||
|
||||
class TestHTTPS(tutils.HTTPProxTest, SanityMixin):
|
||||
class TestHTTPS(tservers.HTTPProxTest, SanityMixin):
|
||||
ssl = True
|
||||
clientcerts = True
|
||||
def test_clientcert(self):
|
||||
@ -77,15 +89,15 @@ class TestHTTPS(tutils.HTTPProxTest, SanityMixin):
|
||||
assert self.last_log()["request"]["clientcert"]["keyinfo"]
|
||||
|
||||
|
||||
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
|
||||
|
183
test/tservers.py
Normal file
183
test/tservers.py
Normal file
@ -0,0 +1,183 @@
|
||||
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
|
||||
|
||||
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=tutils.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"] = tutils.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
|
||||
|
167
test/tutils.py
167
test/tutils.py
@ -1,10 +1,8 @@
|
||||
import threading, Queue
|
||||
import os, shutil, tempfile
|
||||
from contextlib import contextmanager
|
||||
from libmproxy import proxy, flow, controller, utils
|
||||
from libmproxy import flow, utils
|
||||
from netlib import certutils
|
||||
import human_curl as hurl
|
||||
import libpathod.test, libpathod.pathoc
|
||||
|
||||
|
||||
def treq(conn=None):
|
||||
if not conn:
|
||||
@ -42,166 +40,6 @@ def tflow_err():
|
||||
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):
|
||||
orig_workdir = os.getcwd()
|
||||
@ -252,5 +90,4 @@ def raises(exc, obj, *args, **kwargs):
|
||||
)
|
||||
raise AssertionError("No exception raised.")
|
||||
|
||||
|
||||
test_data = utils.Data(__name__)
|
||||
|
Loading…
Reference in New Issue
Block a user