mitmproxy/test/tservers.py

370 lines
9.5 KiB
Python
Raw Normal View History

import os.path
2015-05-30 00:03:28 +00:00
import threading
import Queue
import shutil
import tempfile
import flask
2014-08-08 02:43:44 +00:00
import mock
2014-03-09 20:51:24 +00:00
from libmproxy.proxy.config import ProxyConfig
from libmproxy.proxy.server import ProxyServer
from libmproxy.proxy.primitives import TransparentProxyMode
2015-05-30 00:03:28 +00:00
import libpathod.test
import libpathod.pathoc
2014-03-09 20:51:24 +00:00
from libmproxy import flow, controller
from libmproxy.cmdline import APP_HOST, APP_PORT
import tutils
testapp = flask.Flask(__name__)
2015-05-30 00:03:28 +00:00
@testapp.route("/")
def hello():
return "testapp"
2015-05-30 00:03:28 +00:00
@testapp.route("/error")
def error():
raise ValueError("An exception...")
def errapp(environ, start_response):
raise ValueError("errapp")
class TestMaster(flow.FlowMaster):
2014-02-07 06:08:59 +00:00
def __init__(self, config):
config.port = 0
s = ProxyServer(config)
state = flow.State()
flow.FlowMaster.__init__(self, s, state)
self.apps.add(testapp, "testapp", 80)
self.apps.add(errapp, "errapp", 80)
self.clear_log()
def handle_request(self, f):
flow.FlowMaster.handle_request(self, f)
f.reply()
2013-02-23 03:34:59 +00:00
def handle_response(self, f):
flow.FlowMaster.handle_response(self, f)
f.reply()
def clear_log(self):
self.log = []
def handle_log(self, l):
self.log.append(l.msg)
l.reply()
class ProxyThread(threading.Thread):
2013-02-23 03:34:59 +00:00
def __init__(self, tmaster):
threading.Thread.__init__(self)
2013-02-23 03:34:59 +00:00
self.tmaster = tmaster
2015-05-30 00:03:28 +00:00
self.name = "ProxyThread (%s:%s)" % (
tmaster.server.address.host, tmaster.server.address.port)
2013-02-23 03:34:59 +00:00
controller.should_exit = False
@property
def port(self):
return self.tmaster.server.address.port
@property
def log(self):
return self.tmaster.log
def run(self):
self.tmaster.run()
def shutdown(self):
self.tmaster.shutdown()
2014-02-07 06:08:59 +00:00
class ProxTestBase(object):
# Test Configuration
ssl = None
ssloptions = False
clientcerts = False
2013-03-02 03:59:16 +00:00
no_upstream_cert = False
2013-03-02 22:04:33 +00:00
authenticator = None
2013-02-23 03:34:59 +00:00
masterclass = TestMaster
@classmethod
def setupAll(cls):
2015-05-30 00:03:28 +00:00
cls.server = libpathod.test.Daemon(
ssl=cls.ssl,
ssloptions=cls.ssloptions)
cls.server2 = libpathod.test.Daemon(
ssl=cls.ssl,
ssloptions=cls.ssloptions)
cls.config = ProxyConfig(**cls.get_proxy_config())
2014-08-08 02:43:44 +00:00
tmaster = cls.masterclass(cls.config)
tmaster.start_app(APP_HOST, APP_PORT)
2013-02-23 03:34:59 +00:00
cls.proxy = ProxyThread(tmaster)
cls.proxy.start()
@classmethod
def teardownAll(cls):
2014-11-15 03:17:05 +00:00
shutil.rmtree(cls.cadir)
cls.proxy.shutdown()
cls.server.shutdown()
cls.server2.shutdown()
def setUp(self):
self.master.clear_log()
self.master.state.clear()
self.server.clear_log()
self.server2.clear_log()
@property
def master(self):
return self.proxy.tmaster
@classmethod
def get_proxy_config(cls):
2014-11-15 03:17:05 +00:00
cls.cadir = os.path.join(tempfile.gettempdir(), "mitmproxy")
return dict(
no_upstream_cert = cls.no_upstream_cert,
2014-11-15 03:17:05 +00:00
cadir = cls.cadir,
authenticator = cls.authenticator,
ssl_ports=([cls.server.port, cls.server2.port] if cls.ssl else []),
clientcerts = tutils.test_data.path("data/clientcert") if cls.clientcerts else None
)
class HTTPProxTest(ProxTestBase):
2013-03-02 03:59:16 +00:00
def pathoc_raw(self):
return libpathod.pathoc.Pathoc(("127.0.0.1", self.proxy.port), fp=None)
2013-03-02 03:59:16 +00:00
def pathoc(self, sni=None):
"""
Returns a connected Pathoc instance.
"""
p = libpathod.pathoc.Pathoc(
("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None
)
2013-03-02 03:59:16 +00:00
if self.ssl:
p.connect(("127.0.0.1", self.server.port))
else:
p.connect()
return p
def pathod(self, spec, sni=None):
"""
2013-02-28 20:05:39 +00:00
Constructs a pathod GET request, with the appropriate base and proxy.
"""
2013-03-02 03:59:16 +00:00
p = self.pathoc(sni=sni)
spec = spec.encode("string_escape")
2013-02-28 20:05:39 +00:00
if self.ssl:
2015-05-30 00:03:28 +00:00
q = "get:'/p/%s'" % spec
2013-02-28 20:05:39 +00:00
else:
2015-05-30 00:03:28 +00:00
q = "get:'%s/p/%s'" % (self.server.urlbase, spec)
2013-02-28 20:05:39 +00:00
return p.request(q)
2013-03-24 20:20:26 +00:00
def app(self, page):
if self.ssl:
p = libpathod.pathoc.Pathoc(
("127.0.0.1", self.proxy.port), True, fp=None
)
2013-12-08 14:46:11 +00:00
p.connect((APP_HOST, APP_PORT))
2015-05-30 00:03:28 +00:00
return p.request("get:'%s'" % page)
2013-03-24 20:20:26 +00:00
else:
p = self.pathoc()
2015-05-30 00:03:28 +00:00
return p.request("get:'http://%s%s'" % (APP_HOST, page))
2013-03-24 20:20:26 +00:00
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
resolver = TResolver
2014-08-08 02:43:44 +00:00
@classmethod
2014-08-08 02:43:44 +00:00
@mock.patch("libmproxy.platform.resolver")
def setupAll(cls, _):
super(TransparentProxTest, cls).setupAll()
2013-02-28 20:05:39 +00:00
if cls.ssl:
ports = [cls.server.port, cls.server2.port]
else:
ports = []
2015-05-30 00:03:28 +00:00
cls.config.mode = TransparentProxyMode(
2015-08-02 08:36:18 +00:00
cls.resolver(cls.server.port),
2015-05-30 00:03:28 +00:00
ports)
2014-08-08 02:43:44 +00:00
@classmethod
def get_proxy_config(cls):
d = ProxTestBase.get_proxy_config()
d["mode"] = "transparent"
return d
2013-03-02 02:06:49 +00:00
def pathod(self, spec, sni=None):
"""
2013-03-02 02:06:49 +00:00
Constructs a pathod GET request, with the appropriate base and proxy.
"""
2013-03-02 02:06:49 +00:00
if self.ssl:
p = self.pathoc(sni=sni)
2015-05-30 00:03:28 +00:00
q = "get:'/p/%s'" % spec
2013-03-02 02:06:49 +00:00
else:
p = self.pathoc()
2015-05-30 00:03:28 +00:00
q = "get:'/p/%s'" % spec
2013-03-02 02:06:49 +00:00
return p.request(q)
2013-03-02 02:06:49 +00:00
def pathoc(self, sni=None):
2013-02-28 20:05:39 +00:00
"""
Returns a connected Pathoc instance.
"""
p = libpathod.pathoc.Pathoc(
("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None
)
2013-03-02 02:06:49 +00:00
p.connect()
2013-02-28 20:05:39 +00:00
return p
class ReverseProxTest(ProxTestBase):
ssl = None
2015-05-30 00:03:28 +00:00
@classmethod
def get_proxy_config(cls):
d = ProxTestBase.get_proxy_config()
2015-02-27 08:17:41 +00:00
d["upstream_server"] = [
True if cls.ssl else False,
True if cls.ssl else False,
"127.0.0.1",
cls.server.port
2015-02-27 08:17:41 +00:00
]
d["mode"] = "reverse"
return d
def pathoc(self, sni=None):
"""
Returns a connected Pathoc instance.
"""
p = libpathod.pathoc.Pathoc(
("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None
)
p.connect()
return p
def pathod(self, spec, sni=None):
"""
Constructs a pathod GET request, with the appropriate base and proxy.
"""
if self.ssl:
p = self.pathoc(sni=sni)
2015-05-30 00:03:28 +00:00
q = "get:'/p/%s'" % spec
else:
p = self.pathoc()
2015-05-30 00:03:28 +00:00
q = "get:'/p/%s'" % spec
return p.request(q)
2013-03-24 20:20:26 +00:00
2015-07-03 00:47:12 +00:00
class SocksModeTest(HTTPProxTest):
@classmethod
def get_proxy_config(cls):
d = ProxTestBase.get_proxy_config()
d["mode"] = "socks5"
return d
2015-06-22 15:57:33 +00:00
class SpoofModeTest(ProxTestBase):
ssl = None
@classmethod
def get_proxy_config(cls):
d = ProxTestBase.get_proxy_config()
d["upstream_server"] = None
d["mode"] = "spoof"
return d
def pathoc(self, sni=None):
"""
Returns a connected Pathoc instance.
"""
p = libpathod.pathoc.Pathoc(
("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None
)
p.connect()
return p
class SSLSpoofModeTest(ProxTestBase):
ssl = True
@classmethod
def get_proxy_config(cls):
d = ProxTestBase.get_proxy_config()
d["upstream_server"] = None
d["mode"] = "sslspoof"
d["spoofed_ssl_port"] = 443
return d
def pathoc(self, sni=None):
"""
Returns a connected Pathoc instance.
"""
p = libpathod.pathoc.Pathoc(
("localhost", self.proxy.port), ssl=self.ssl, sni=sni, fp=None
)
p.connect()
return p
2014-02-07 06:08:59 +00:00
class ChainProxTest(ProxTestBase):
"""
Chain three instances of mitmproxy in a row to test upstream mode.
Proxy order is cls.proxy -> cls.chain[0] -> cls.chain[1]
cls.proxy and cls.chain[0] are in upstream mode,
cls.chain[1] is in regular mode.
2014-02-07 06:08:59 +00:00
"""
chain = None
2014-02-07 06:08:59 +00:00
n = 2
2014-02-07 06:08:59 +00:00
@classmethod
def setupAll(cls):
cls.chain = []
super(ChainProxTest, cls).setupAll()
for _ in range(cls.n):
config = ProxyConfig(**cls.get_proxy_config())
2014-02-07 06:08:59 +00:00
tmaster = cls.masterclass(config)
proxy = ProxyThread(tmaster)
proxy.start()
cls.chain.insert(0, proxy)
# Patch the orginal proxy to upstream mode
2015-05-30 00:03:28 +00:00
cls.config = cls.proxy.tmaster.config = cls.proxy.tmaster.server.config = ProxyConfig(
**cls.get_proxy_config())
2014-02-07 06:08:59 +00:00
@classmethod
def teardownAll(cls):
super(ChainProxTest, cls).teardownAll()
for proxy in cls.chain:
proxy.shutdown()
2013-03-24 20:20:26 +00:00
def setUp(self):
super(ChainProxTest, self).setUp()
for proxy in self.chain:
proxy.tmaster.clear_log()
proxy.tmaster.state.clear()
@classmethod
def get_proxy_config(cls):
d = super(ChainProxTest, cls).get_proxy_config()
if cls.chain: # First proxy is in normal mode.
d.update(
mode="upstream",
upstream_server=(False, False, "127.0.0.1", cls.chain[0].port)
)
return d
2013-03-24 20:20:26 +00:00
2015-05-30 00:03:28 +00:00
class HTTPUpstreamProxTest(ChainProxTest, HTTPProxTest):
pass