diff options
Diffstat (limited to 'test/test_server.py')
-rw-r--r-- | test/test_server.py | 259 |
1 files changed, 236 insertions, 23 deletions
diff --git a/test/test_server.py b/test/test_server.py index 0a2f142e..c20b0cea 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -1,7 +1,9 @@ import socket, time -from netlib import tcp +import mock +from netlib import tcp, http_auth, http 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 @@ -11,11 +13,7 @@ import tutils for a 200 response. """ -class SanityMixin: - def test_http(self): - assert self.pathod("304").status_code == 304 - assert self.master.state.view - +class CommonMixin: def test_large(self): assert len(self.pathod("200:b@50k").content) == 1024*50 @@ -38,15 +36,39 @@ class SanityMixin: self.master.replay_request(l, block=True) assert l.error + def test_http(self): + f = self.pathod("304") + assert f.status_code == 304 + + l = self.master.state.view[0] + assert l.request.client_conn.address + assert "host" in l.request.headers + assert l.response.code == 304 -class TestHTTP(tutils.HTTPProxTest, SanityMixin): def test_invalid_http(self): t = tcp.TCPClient("127.0.0.1", self.proxy.port) t.connect() - t.wfile.write("invalid\n\n") + t.wfile.write("invalid\r\n\r\n") t.wfile.flush() assert "Bad Request" in t.rfile.readline() + + +class AppMixin: + def test_app(self): + ret = self.app("/") + assert ret.status_code == 200 + assert "mitmproxy" in ret.content + + + +class TestHTTP(tservers.HTTPProxTest, CommonMixin, AppMixin): + def test_app_err(self): + p = self.pathoc() + ret = p.request("get:'http://errapp/'") + assert ret.status_code == 500 + assert "ValueError" in ret.content + def test_invalid_connect(self): t = tcp.TCPClient("127.0.0.1", self.proxy.port) t.connect() @@ -57,35 +79,157 @@ class TestHTTP(tutils.HTTPProxTest, SanityMixin): def test_upstream_ssl_error(self): p = self.pathoc() ret = p.request("get:'https://localhost:%s/'"%self.server.port) - assert ret[1] == 400 + assert ret.status_code == 400 - def test_http(self): - f = self.pathod("304") - assert f.status_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 - l = self.master.state.view[0] - assert l.request.client_conn.address - assert "host" in l.request.headers - assert l.response.code == 304 + # 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) + + # 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("server disconnect", 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.status_code == 502 + + 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 + + def test_invalid_headers(self): + p = self.pathoc() + req = p.request("get:'http://foo':h':foo'='bar'") + assert req.status_code == 400 + + + +class TestHTTPAuth(tservers.HTTPProxTest): + authenticator = http_auth.BasicProxyAuth(http_auth.PassManSingleUser("test", "test"), "realm") + def test_auth(self): + 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_auth.BasicProxyAuth.AUTH_HEADER, + http.assemble_http_basic_auth("basic", "test", "test") + )) + assert ret.status_code == 202 -class TestHTTPS(tutils.HTTPProxTest, SanityMixin): +class TestHTTPConnectSSLError(tservers.HTTPProxTest): + certfile = True + def test_go(self): + p = self.pathoc() + req = "connect:'localhost:%s'"%self.proxy.port + assert p.request(req).status_code == 200 + assert p.request(req).status_code == 400 + + +class TestHTTPS(tservers.HTTPProxTest, CommonMixin): ssl = True clientcerts = True def test_clientcert(self): f = self.pathod("304") - assert self.last_log()["request"]["clientcert"]["keyinfo"] + assert f.status_code == 304 + assert self.server.last_log()["request"]["clientcert"]["keyinfo"] + + def test_sni(self): + f = self.pathod("304", sni="testserver.com") + assert f.status_code == 304 + l = self.server.last_log() + assert self.server.last_log()["request"]["sni"] == "testserver.com" + + def test_error_post_connect(self): + p = self.pathoc() + assert p.request("get:/:i0,'invalid\r\n\r\n'").status_code == 400 + + +class TestHTTPSNoUpstream(tservers.HTTPProxTest, CommonMixin): + ssl = True + no_upstream_cert = True + def test_cert_gen_error(self): + f = self.pathoc_raw() + f.connect((u"foo..bar".encode("utf8"), 0)) + f.request("get:/") + assert "dummy cert" in "".join(self.proxy.log) -class TestReverse(tutils.ReverseProxTest, SanityMixin): +class TestHTTPSCertfile(tservers.HTTPProxTest, CommonMixin): + ssl = True + certfile = True + def test_certfile(self): + assert self.pathod("304") + + +class TestReverse(tservers.ReverseProxTest, CommonMixin): reverse = True -class TestTransparent(tutils.TransparentProxTest, SanityMixin): - transparent = True +class TestTransparent(tservers.TransparentProxTest, CommonMixin): + ssl = False + + +class TestTransparentSSL(tservers.TransparentProxTest, CommonMixin): + ssl = True + def test_sni(self): + f = self.pathod("304", sni="testserver.com") + assert f.status_code == 304 + l = self.server.last_log() + assert self.server.last_log()["request"]["sni"] == "testserver.com" + def test_sslerr(self): + p = pathoc.Pathoc("localhost", self.proxy.port) + p.connect() + assert p.request("get:/").status_code == 400 -class TestProxy(tutils.HTTPProxTest): + +class TestProxy(tservers.HTTPProxTest): def test_http(self): f = self.pathod("304") assert f.status_code == 304 @@ -132,3 +276,72 @@ 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_fake(self): + 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): + tutils.raises("server disconnect", 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): + tutils.raises("server disconnect", self.pathod, "200") + # The server should have seen a request + assert self.server.last_log() + + +class EResolver(tservers.TResolver): + def original_addr(self, sock): + return None + + +class TestTransparentResolveError(tservers.TransparentProxTest): + resolver = EResolver + def test_resolve_error(self): + assert self.pathod("304").status_code == 502 + + + +class MasterIncomplete(tservers.TestMaster): + def handle_request(self, m): + resp = tutils.tresp() + resp.content = flow.CONTENT_MISSING + m.reply(resp) + + +class TestIncompleteResponse(tservers.HTTPProxTest): + masterclass = MasterIncomplete + def test_incomplete(self): + assert self.pathod("200").status_code == 502 + + + |