import pytest import mock import random import datetime import pappyproxy import base64 from pappyproxy import http from pappyproxy.proxy import ProxyClientFactory, ProxyServerFactory, UpstreamHTTPProxyClient from testutil import mock_deferred, func_deleted, TLSStringTransport, freeze, mock_int_macro, no_tcp from twisted.internet import defer @pytest.fixture(autouse=True) def proxy_patches(mocker): #mocker.patch("twisted.test.iosim.FakeTransport.startTLS") mocker.patch("pappyproxy.proxy.load_certs_from_dir", new=mock_generate_cert) @pytest.fixture def server_factory(): return gen_server_factory() @pytest.fixture(autouse=True) def mock_config(mocker): c = pappyproxy.config.PappyConfig() s = pappyproxy.pappy.PappySession(c) mocker.patch.object(pappyproxy.pappy, 'session', new=s) def socks_config(mocker, config): pappyproxy.pappy.session.config.socks_proxy = config def http_proxy_config(mocker, config): pappyproxy.pappy.session.config.http_proxy = config def gen_server_factory(int_macros={}): factory = ProxyServerFactory() factory.save_all = True factory.intercepting_macros = int_macros return factory def gen_server_protocol(int_macros={}): server_factory = gen_server_factory(int_macros=int_macros) protocol = server_factory.buildProtocol(('127.0.0.1', 0)) tr = TLSStringTransport() protocol.makeConnection(tr) return protocol @defer.inlineCallbacks def gen_client_protocol(req, stream_response=False, save_all=True): return_transport = TLSStringTransport() factory = ProxyClientFactory(req, save_all=save_all, stream_response=stream_response, return_transport=return_transport) yield factory.prepare_request() protocol = factory.buildProtocol(('127.0.0.1', 0), _do_callback=False) tr = TLSStringTransport() protocol.makeConnection(tr) defer.returnValue(protocol) @pytest.fixture def server_protocol(): return gen_server_protocol() def mock_req_async_save(req): req.reqid = str(random.randint(1,1000000)) return mock_deferred() def mock_mangle_response_side_effect(new_rsp): def f(request, mangle_macros): request.response = new_rsp return mock_deferred(True) return f #################### ## Mock functions def mock_generate_cert(cert_dir): private_key = ('-----BEGIN PRIVATE KEY-----\n' 'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAoClrYUEB7lM0\n' 'zQaKkXZVG2d1Bu9hV8urpx0gNXMbyZ2m3xb+sKZju/FHPuWenA4KaN5gRUT+oLfv\n' 'tnF6Ia0jpRNWnX0Fyn/irdg1BWGJn7k7mJ2D0NXZQczn2+xxY05599NfGWqNKCYy\n' 'jhSwPsUK+sGJqi7aSDdlS97ZTjrQVTTFsC0+kSu4lS5fsWXxqrKLa6Ao8W7abVRO\n' 'JHazh/cxM4UKpgWU+E6yD4o4ZgHY+SMTVUh/IOM8DuOVyLEWtx4oLNiLMlpWT3qy\n' '4IMpOF6VuU6JF2HGV13SoJfhsLXsPRbLVTAnZvJcZwtgDm6NfKapU8W8olkDV1Bf\n' 'YQEMSNX7AgMBAAECggEBAII0wUrAdrzjaIMsg9tu8FofKBPHGFDok9f4Iov/FUwX\n' 'QOXnrxeCOTb5d+L89SH9ws/ui0LwD+8+nJcA8DvqP6r0jtnhov0jIMcNVDSi6oeo\n' '3AEY7ICJzcQJ4oRn+K+8vPNdPhfuikPYe9l4iSuJgpAlaGWyD/GlFyz12DFz2/Wu\n' 'NIcqR1ucvezRHn3eGMtvDv2WGaN4ifUc30k8XgSUesmwSI6beb5+hxq7wXfsurnP\n' 'EUrPY9ts3lfiAgxzTKOuj1VR5hn7cJyLN8jF0mZs4D6eSSHorIddhmaNiCq5ZbMd\n' 'QdlDiPvnXHT41OoXOb7tDEt7SGoiRh2noCZ1aZiSziECgYEA+tuPPLYWU6JRB6EW\n' 'PhbcXQbh3vML7eT1q7DOz0jYCojgT2+k7EWSI8T830oQyjbpe3Z86XEgH7UBjUgq\n' '27nJ4E6dQDYGbYCKEklOoCGLE7A60i1feIz8otOQRrbQ4jcpibEgscA6gzHmunYf\n' 'De5euUgYW+Rq2Vmr6/NzUaUgui8CgYEAxJMDwPOGgiLM1cczlaSIU9Obz+cVnwWn\n' 'nsdKYMto2V3yKLydDfjsgOgzxHOxxy+5L645TPxK6CkiISuhJ93kAFFtx+1sCBCT\n' 'tVzY5robVAekxA9tlPIxtsn3+/axx3n6HnV0oA/XtxkuOS5JImgEdXqFwJZkerGE\n' 'waftIU2FCfUCgYEArl8+ErJzlJEIiCgWIPSdGuD00pfZW/TCPCT7rKRy3+fDHBR7\n' '7Gxzp/9+0utV/mnrJBH5w/8JmGCmgoF+oRtk01FyBzdGgolN8GYajD6kwPvH917o\n' 'tRAzcC9lY3IigoxbiEWid0wqoBVoz4XaEkH2gA44OG/vQcQOOEYSi9cfh6sCgYBg\n' 'KLaOXdJvuIxRCzgNvMW/k+VFh3pJJx//COg2f2qT4mQCT3nYiutOh8hDEoFluc+y\n' 'Jlz7bvNJrE14wnn8IYxWJ383bMoLC+jlsDyeaW3S5kZQbmehk/SDwTrg86W1udKD\n' 'sdtSLU3N0LCO4jh+bzm3Ki9hrXALoOkbPoU+ZEhvPQKBgQDf79XQ3RNxZSk+eFyq\n' 'qD8ytVqxEoD+smPDflXXseVH6o+pNWrF8+A0KqmO8c+8KVzWj/OfULO6UbKd3E+x\n' '4JGkWu9yF1lEgtHgibF2ER8zCSIL4ikOEasPCkrKj5SrS4Q+j4u5ha76dIc2CVu1\n' 'hkX2PQ1xU4ocu06k373sf73A4Q==\n' '-----END PRIVATE KEY-----') ca_key = ('-----BEGIN CERTIFICATE-----\n' 'MIIDjzCCAncCFQCjC8r+I4xa7JoGUJYGOTcqDROA0DANBgkqhkiG9w0BAQsFADBg\n' 'MQswCQYDVQQGEwJVUzERMA8GA1UECBMITWljaGlnYW4xEjAQBgNVBAcTCUFubiBB\n' 'cmJvcjEUMBIGA1UEChMLUGFwcHkgUHJveHkxFDASBgNVBAMTC1BhcHB5IFByb3h5\n' 'MB4XDTE1MTEyMDIxMTEzOVoXDTI1MTExNzIxMTEzOVowYDELMAkGA1UEBhMCVVMx\n' 'ETAPBgNVBAgTCE1pY2hpZ2FuMRIwEAYDVQQHEwlBbm4gQXJib3IxFDASBgNVBAoT\n' 'C1BhcHB5IFByb3h5MRQwEgYDVQQDEwtQYXBweSBQcm94eTCCASIwDQYJKoZIhvcN\n' 'AQEBBQADggEPADCCAQoCggEBAMCgKWthQQHuUzTNBoqRdlUbZ3UG72FXy6unHSA1\n' 'cxvJnabfFv6wpmO78Uc+5Z6cDgpo3mBFRP6gt++2cXohrSOlE1adfQXKf+Kt2DUF\n' 'YYmfuTuYnYPQ1dlBzOfb7HFjTnn3018Zao0oJjKOFLA+xQr6wYmqLtpIN2VL3tlO\n' 'OtBVNMWwLT6RK7iVLl+xZfGqsotroCjxbtptVE4kdrOH9zEzhQqmBZT4TrIPijhm\n' 'Adj5IxNVSH8g4zwO45XIsRa3Higs2IsyWlZPerLggyk4XpW5TokXYcZXXdKgl+Gw\n' 'tew9FstVMCdm8lxnC2AObo18pqlTxbyiWQNXUF9hAQxI1fsCAwEAAaNFMEMwEgYD\n' 'VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNo5o+5e\n' 'a0sNMlW/75VgGJCv2AcJMA0GCSqGSIb3DQEBCwUAA4IBAQBdJDhxbmoEe27bD8me\n' 'YTcLGjs/StKkSil7rLbX+tBCwtkm5UEEejBuAhKk2FuAXW8yR1FqKJSZwVCAocBT\n' 'Bo/+97Ee+h7ywrRFhATEr9D/TbbHKOjCjDzOMl9yLZa2DKErZjbI30ZD6NafWS/X\n' 'hx5X1cGohHcVVzT4jIgUEU70vvYfNn8CTZm4oJ7qqRe/uQPUYy0rwvbd60oprtGg\n' 'jNv1H5R4ODHUMBXAI9H7ft9cWrd0fBQjxhoj8pvgJXEZ52flXSqQc7qHLg1wO/zC\n' 'RUgpTcNAb2qCssBKbj+c1vKEPRUJfw6UYb0s1462rQNc8BgZiKaNbwokFmkAnjUg\n' 'AvnX\n' '-----END CERTIFICATE-----') return (ca_key, private_key) ######## ## Tests def test_no_tcp(): from twisted.internet.endpoints import SSL4ClientEndpoint, TCP4ClientEndpoint from txsocksx.client import SOCKS5ClientEndpoint from txsocksx.tls import TLSWrapClientEndpoint with pytest.raises(NotImplementedError): SSL4ClientEndpoint('aasdfasdf.sdfwerqwer') with pytest.raises(NotImplementedError): TCP4ClientEndpoint('aasdfasdf.sdfwerqwer') with pytest.raises(NotImplementedError): SOCKS5ClientEndpoint('aasdfasdf.sdfwerqwer') with pytest.raises(NotImplementedError): TLSWrapClientEndpoint('asdf.2341') ################ ### Proxy Server def test_proxy_server_connect(mocker, server_protocol): mstarttls = mocker.patch('pappyproxy.tests.testutil.TLSStringTransport.startTLS') server_protocol.dataReceived('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') assert server_protocol.transport.value() == 'HTTP/1.1 200 Connection established\r\n\r\n' assert mstarttls.called def test_proxy_server_forward_basic(mocker, server_protocol): mforward = mocker.patch('pappyproxy.proxy.ProxyServer._generate_and_submit_client') mreset = mocker.patch('pappyproxy.proxy.ProxyServer._reset') req_contents = ('POST /fooo HTTP/1.1\r\n' 'Test-Header: foo\r\n' 'Content-Length: 4\r\n' '\r\n' 'ABCD') server_protocol.dataReceived(req_contents) assert mforward.called assert mreset.called assert server_protocol._request_obj.full_message == req_contents def test_proxy_server_connect_uri(mocker, server_protocol): mforward = mocker.patch('pappyproxy.proxy.ProxyServer._generate_and_submit_client') server_protocol.dataReceived('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') server_protocol.dataReceived('GET /fooo HTTP/1.1\r\nTest-Header: foo\r\n\r\n') assert server_protocol._connect_uri == 'https://www.AAAA.BBBB' assert server_protocol._request_obj.url == 'https://www.AAAA.BBBB' assert server_protocol._request_obj.port == 443 ## ProxyServer._generate_and_submit_client def test_proxy_server_create_client_factory(mocker, server_protocol): mfactory = mock.MagicMock() mfactory_class = mocker.patch('pappyproxy.proxy.ProxyClientFactory') mfactory_class.return_value = mfactory mocker.patch('pappyproxy.proxy.ProxyServer._make_remote_connection') mfactory.prepare_request.return_value = mock_deferred(None) full_req = ('POST /fooo HTTP/1.1\r\n' 'Test-Header: foo\r\n' 'Content-Length: 4\r\n' '\r\n' 'ABCD') server_protocol.connection_id = 100 server_protocol.dataReceived(full_req) # Make sure we created a ClientFactory with the right arguments f_args, f_kwargs = mfactory_class.call_args assert len(f_args) == 1 # Make sure the request got to the client class req = f_args[0] assert req.full_message == full_req # Make sure the correct settings got to the proxy assert f_kwargs['stream_response'] == True assert f_kwargs['save_all'] == True # Make sure we initialized the client factory assert mfactory.prepare_request.called assert mfactory.connection_id == 100 assert server_protocol._make_remote_connection.called # should be immediately called because mock deferred def test_proxy_server_no_streaming_with_int_macros(mocker): mfactory = mock.MagicMock() mfactory_class = mocker.patch('pappyproxy.proxy.ProxyClientFactory') mfactory_class.return_value = mfactory mocker.patch('pappyproxy.proxy.ProxyServer._make_remote_connection') mfactory.prepare_request.return_value = mock_deferred(None) full_req = ('POST /fooo HTTP/1.1\r\n' 'Test-Header: foo\r\n' 'Content-Length: 4\r\n' '\r\n' 'ABCD') int_macros = [{'mockmacro': mock_int_macro(modified_req='GET / HTTP/1.1\r\n\r\n')}] server_protocol = gen_server_protocol(int_macros=int_macros) server_protocol.dataReceived(full_req) f_args, f_kwargs = mfactory_class.call_args assert f_kwargs['stream_response'] == False ## ProxyServer._make_remote_connection @pytest.inlineCallbacks def test_proxy_server_make_tcp_connection(mocker, server_protocol): mtcpe_class = mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint") mtcpe_class.return_value = mtcpe = mock.MagicMock() mtcpe.connect.return_value = mock_deferred() server_protocol._client_factory = mock.MagicMock() # We already tested that this gets set up correctly req = http.Request("GET / HTTP/1.1\r\n\r\n") req.host = 'Foo.Bar.Brazzers' req.port = 80085 server_protocol._request_obj = req yield server_protocol._make_remote_connection(req) targs, tkwargs = mtcpe_class.call_args assert targs[1] == 'Foo.Bar.Brazzers' assert targs[2] == 80085 assert tkwargs == {} mtcpe.connect.assert_called_once_with(server_protocol._client_factory) @pytest.inlineCallbacks def test_proxy_server_make_ssl_connection(mocker, server_protocol): mssle_class = mocker.patch("twisted.internet.endpoints.SSL4ClientEndpoint") mssle_class.return_value = mssle = mock.MagicMock() mssle.connect.return_value = mock_deferred() server_protocol._client_factory = mock.MagicMock() # We already tested that this gets set up correctly req = http.Request("GET / HTTP/1.1\r\n\r\n", is_ssl=True) req.host = 'Foo.Bar.Brazzers' req.port = 80085 server_protocol._request_obj = req yield server_protocol._make_remote_connection(req) targs, tkwargs = mssle_class.call_args assert targs[1] == 'Foo.Bar.Brazzers' assert targs[2] == 80085 assert tkwargs == {} mssle.connect.assert_called_once_with(server_protocol._client_factory) @pytest.inlineCallbacks def test_proxy_server_make_tcp_connection_socks(mocker): socks_config(mocker, {'host': '12345', 'port': 5555}) tls_wrap_class = mocker.patch("txsocksx.tls.TLSWrapClientEndpoint") mtcpe_class = mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint") mtcpe_class.return_value = mtcpe = mock.MagicMock() socks_class = mocker.patch("txsocksx.client.SOCKS5ClientEndpoint") socks_class.return_value = sockse = mock.MagicMock() server_protocol = gen_server_protocol() server_protocol._client_factory = mock.MagicMock() # We already tested that this gets set up correctly req = http.Request("GET / HTTP/1.1\r\n\r\n") req.host = 'Foo.Bar.Brazzers' req.port = 80085 server_protocol._request_obj = req yield server_protocol._make_remote_connection(req) sargs, skwargs = socks_class.call_args targs, tkwargs = mtcpe_class.call_args assert targs[1] == '12345' assert targs[2] == 5555 assert sargs[0] == 'Foo.Bar.Brazzers' assert sargs[1] == 80085 assert sargs[2] == mtcpe assert skwargs == {'methods': {'anonymous': ()}} assert not tls_wrap_class.called sockse.connect.assert_called_once_with(server_protocol._client_factory) @pytest.inlineCallbacks def test_proxy_server_make_ssl_connection_socks(mocker): socks_config(mocker, {'host': '12345', 'port': 5555}) tls_wrap_class = mocker.patch("txsocksx.tls.TLSWrapClientEndpoint") tls_wrape = tls_wrap_class.return_value = mock.MagicMock() mtcpe_class = mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint") mtcpe_class.return_value = mtcpe = mock.MagicMock() socks_class = mocker.patch("txsocksx.client.SOCKS5ClientEndpoint") socks_class.return_value = sockse = mock.MagicMock() server_protocol = gen_server_protocol() server_protocol._client_factory = mock.MagicMock() # We already tested that this gets set up correctly req = http.Request("GET / HTTP/1.1\r\n\r\n") req.host = 'Foo.Bar.Brazzers' req.port = 80085 req.is_ssl = True server_protocol._request_obj = req yield server_protocol._make_remote_connection(req) sargs, skwargs = socks_class.call_args targs, tkwargs = mtcpe_class.call_args assert targs[1] == '12345' assert targs[2] == 5555 assert sargs[0] == 'Foo.Bar.Brazzers' assert sargs[1] == 80085 assert sargs[2] == mtcpe assert skwargs == {'methods': {'anonymous': ()}} assert not sockse.called tls_wrape.connect.assert_called_once_with(server_protocol._client_factory) @pytest.inlineCallbacks def test_proxy_server_make_ssl_connection_socks_username_only(mocker): socks_config(mocker, {'host': '12345', 'port': 5555, 'username': 'foo'}) tls_wrap_class = mocker.patch("txsocksx.tls.TLSWrapClientEndpoint") tls_wrape = tls_wrap_class.return_value = mock.MagicMock() mtcpe_class = mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint") mtcpe_class.return_value = mtcpe = mock.MagicMock() socks_class = mocker.patch("txsocksx.client.SOCKS5ClientEndpoint") socks_class.return_value = sockse = mock.MagicMock() server_protocol = gen_server_protocol() server_protocol._client_factory = mock.MagicMock() # We already tested that this gets set up correctly req = http.Request("GET / HTTP/1.1\r\n\r\n") req.host = 'Foo.Bar.Brazzers' req.port = 80085 req.is_ssl = True server_protocol._request_obj = req yield server_protocol._make_remote_connection(req) sargs, skwargs = socks_class.call_args targs, tkwargs = mtcpe_class.call_args assert targs[1] == '12345' assert targs[2] == 5555 assert sargs[0] == 'Foo.Bar.Brazzers' assert sargs[1] == 80085 assert sargs[2] == mtcpe assert skwargs == {'methods': {'anonymous': ()}} assert not sockse.called tls_wrape.connect.assert_called_once_with(server_protocol._client_factory) @pytest.inlineCallbacks def test_proxy_server_make_ssl_connection_socks_username_password(mocker): socks_config(mocker, {'host': '12345', 'port': 5555, 'username': 'foo', 'password': 'password'}) tls_wrap_class = mocker.patch("txsocksx.tls.TLSWrapClientEndpoint") tls_wrape = tls_wrap_class.return_value = mock.MagicMock() mtcpe_class = mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint") mtcpe_class.return_value = mtcpe = mock.MagicMock() socks_class = mocker.patch("txsocksx.client.SOCKS5ClientEndpoint") socks_class.return_value = sockse = mock.MagicMock() server_protocol = gen_server_protocol() server_protocol._client_factory = mock.MagicMock() # We already tested that this gets set up correctly req = http.Request("GET / HTTP/1.1\r\n\r\n") req.host = 'Foo.Bar.Brazzers' req.port = 80085 req.is_ssl = True server_protocol._request_obj = req yield server_protocol._make_remote_connection(req) sargs, skwargs = socks_class.call_args targs, tkwargs = mtcpe_class.call_args assert targs[1] == '12345' assert targs[2] == 5555 assert sargs[0] == 'Foo.Bar.Brazzers' assert sargs[1] == 80085 assert sargs[2] == mtcpe assert skwargs == {'methods': {'login': ('foo','password'), 'anonymous': ()}} assert not sockse.called tls_wrape.connect.assert_called_once_with(server_protocol._client_factory) ######################## ### Proxy Client Factory @pytest.inlineCallbacks def test_proxy_client_factory_prepare_reqs_simple(mocker, freeze): import datetime freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) req = http.Request('GET / HTTP/1.1\r\n\r\n') rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) rsave.return_value = mock_deferred() mocker.patch('pappyproxy.context.in_scope').return_value = True mocker.patch('pappyproxy.macros.mangle_request').return_value = mock_deferred((req, False)) cf = ProxyClientFactory(req, save_all=False, stream_response=False, return_transport=None) yield cf.prepare_request() assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert req.reqid is None assert not rsave.called assert len(rsave.mock_calls) == 0 @pytest.inlineCallbacks def test_proxy_client_factory_prepare_reqs_360_noscope(mocker, freeze): import datetime freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) req = http.Request('GET / HTTP/1.1\r\n\r\n') rsave = mocker.patch('pappyproxy.http.Request.async_deep_save') rsave.return_value = mock_deferred() mocker.patch('pappyproxy.context.in_scope').return_value = False mocker.patch('pappyproxy.macros.mangle_request', new=func_deleted) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) yield cf.prepare_request() assert req.time_start == None assert req.reqid is None assert not rsave.called assert len(rsave.mock_calls) == 0 @pytest.inlineCallbacks def test_proxy_client_factory_prepare_reqs_save(mocker, freeze): freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) req = http.Request('GET / HTTP/1.1\r\n\r\n') rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = True mocker.patch('pappyproxy.macros.mangle_request').return_value = mock_deferred((req, False)) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) yield cf.prepare_request() assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert req.reqid is not None assert rsave.called assert len(rsave.mock_calls) == 1 @pytest.inlineCallbacks def test_proxy_client_factory_prepare_reqs_360_noscope_save(mocker, freeze): freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) req = http.Request('GET / HTTP/1.1\r\n\r\n') mangreq = http.Request('BOOO / HTTP/1.1\r\n\r\n') rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = False mocker.patch('pappyproxy.macros.mangle_request', side_effect=func_deleted) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) yield cf.prepare_request() assert req.time_start == None assert req.reqid is None assert not rsave.called assert len(rsave.mock_calls) == 0 @pytest.inlineCallbacks def test_proxy_client_factory_prepare_mangle_req(mocker, freeze): freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) req = http.Request('GET / HTTP/1.1\r\n\r\n') mangreq = http.Request('BOOO / HTTP/1.1\r\n\r\n') def inc_day_mangle(x, y): freeze.delta(days=1) return mock_deferred((mangreq, True)) rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = True mocker.patch('pappyproxy.macros.mangle_request', side_effect=inc_day_mangle) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) yield cf.prepare_request() assert cf.request == mangreq assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert cf.request.time_start == datetime.datetime(2015, 1, 2, 3, 30, 15, 50) assert cf.request.reqid is not None assert len(rsave.mock_calls) == 2 @pytest.inlineCallbacks def test_proxy_client_factory_prepare_mangle_req_drop(mocker, freeze): freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) def inc_day_mangle(x, y): freeze.delta(days=1) return mock_deferred((None, True)) req = http.Request('GET / HTTP/1.1\r\n\r\n') rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = True mocker.patch('pappyproxy.macros.mangle_request', side_effect=inc_day_mangle) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) yield cf.prepare_request() assert cf.request is None assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert len(rsave.mock_calls) == 1 @pytest.inlineCallbacks def test_proxy_client_factory_prepare_mangle_req(mocker, freeze): freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) req = http.Request('GET / HTTP/1.1\r\n\r\n') mangreq = http.Request('BOOO / HTTP/1.1\r\n\r\n') def inc_day_mangle(x, y): freeze.delta(days=1) return mock_deferred((mangreq, True)) rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = True mocker.patch('pappyproxy.macros.mangle_request', side_effect=inc_day_mangle) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) yield cf.prepare_request() assert cf.request == mangreq assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert cf.request.time_start == datetime.datetime(2015, 1, 2, 3, 30, 15, 50) assert cf.request.reqid is not None assert len(rsave.mock_calls) == 2 ### return_request_pair @pytest.inlineCallbacks def test_proxy_client_factory_return_request_pair_simple(mocker, freeze): """ Make sure the proxy doesn't do anything if the request is out of scope """ freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = False req = http.Request('GET / HTTP/1.1\r\n\r\n') req.reqid = 1 rsp = http.Response('HTTP/1.1 200 OK\r\n\r\n') checkrsp = rsp.copy() req.response = rsp mocker.patch('pappyproxy.macros.mangle_response').return_value = mock_deferred(False) cf = ProxyClientFactory(req, save_all=False, stream_response=False, return_transport=None) cf.start_time = datetime.datetime(2015, 1, 1, 3, 30, 14, 50) cf.return_request_pair(req) result = yield cf.data_defer assert result == req assert result.response == checkrsp assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 14, 50) assert req.time_end == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert len(rsave.mock_calls) == 0 @pytest.inlineCallbacks def test_proxy_client_factory_return_request_pair_mangle(mocker, freeze): """ Make one modification to the response """ freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = True req = http.Request('GET / HTTP/1.1\r\n\r\n') req.reqid = 1 rsp = http.Response('HTTP/1.1 200 OK\r\n\r\n') req.response = rsp new_rsp = http.Response('HTTP/1.1 6969 LOLMANGLED\r\n\r\n') checkrsp = new_rsp.copy() mocker.patch('pappyproxy.macros.mangle_response', side_effect=mock_mangle_response_side_effect(new_rsp)) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) cf.start_time = datetime.datetime(2015, 1, 1, 3, 30, 14, 50) cf.return_request_pair(req) result = yield cf.data_defer assert result == req assert result.response == checkrsp assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 14, 50) assert req.time_end == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert len(rsave.mock_calls) == 2 @pytest.inlineCallbacks def test_proxy_client_factory_return_request_pair_no_save_all(mocker, freeze): """ Make one modification to the response but don't save it """ freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = True req = http.Request('GET / HTTP/1.1\r\n\r\n') req.reqid = 1 rsp = http.Response('HTTP/1.1 200 OK\r\n\r\n') req.response = rsp new_rsp = http.Response('HTTP/1.1 6969 LOLMANGLED\r\n\r\n') checkrsp = new_rsp.copy() mocker.patch('pappyproxy.macros.mangle_response', side_effect=mock_mangle_response_side_effect(new_rsp)).return_value = mock_deferred(True) cf = ProxyClientFactory(req, save_all=False, stream_response=False, return_transport=None) cf.start_time = datetime.datetime(2015, 1, 1, 3, 30, 14, 50) cf.return_request_pair(req) result = yield cf.data_defer assert result == req assert result.response == checkrsp assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 14, 50) assert req.time_end == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert len(rsave.mock_calls) == 0 @pytest.inlineCallbacks def test_proxy_client_factory_return_request_pair_save_all_no_mangle(mocker, freeze): """ Make one modification to the response but don't save it """ freeze.freeze(datetime.datetime(2015, 1, 1, 3, 30, 15, 50)) rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) mocker.patch('pappyproxy.context.in_scope').return_value = True req = http.Request('GET / HTTP/1.1\r\n\r\n') req.reqid = 1 rsp = http.Response('HTTP/1.1 200 OK\r\n\r\n') checkrsp = rsp.copy() req.response = rsp mocker.patch('pappyproxy.macros.mangle_response').return_value = mock_deferred(False) cf = ProxyClientFactory(req, save_all=True, stream_response=False, return_transport=None) cf.start_time = datetime.datetime(2015, 1, 1, 3, 30, 14, 50) cf.return_request_pair(req) result = yield cf.data_defer assert result == req assert result.response == checkrsp assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 14, 50) assert req.time_end == datetime.datetime(2015, 1, 1, 3, 30, 15, 50) assert len(rsave.mock_calls) == 1 @pytest.inlineCallbacks def test_proxy_client_factory_build_protocol_http_proxy(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345}) r = http.Request('GET / HTTP/1.1\r\n\r\n') cf = ProxyClientFactory(r, save_all=False, stream_response=False, return_transport=None) yield cf.prepare_request() p = cf.buildProtocol('') assert isinstance(p, UpstreamHTTPProxyClient) assert p.creds is None assert p.proxy_connected == False @pytest.inlineCallbacks def test_proxy_client_factory_build_protocol_http_proxy_username_only(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345, 'username': 'foo'}) r = http.Request('GET / HTTP/1.1\r\n\r\n') cf = ProxyClientFactory(r, save_all=False, stream_response=False, return_transport=None) yield cf.prepare_request() p = cf.buildProtocol('') assert p.creds is None @pytest.inlineCallbacks def test_proxy_client_factory_build_protocol_http_proxy_username_only(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345, 'username': 'foo', 'password': 'password'}) r = http.Request('GET / HTTP/1.1\r\n\r\n') cf = ProxyClientFactory(r, save_all=False, stream_response=False, return_transport=None) yield cf.prepare_request() p = cf.buildProtocol('') assert p.creds == ('foo', 'password') @pytest.inlineCallbacks def test_proxy_upstream_client_connection_made(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345}) r = http.Request(('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n')) p = yield gen_client_protocol(r, save_all=False) assert isinstance(p, UpstreamHTTPProxyClient) assert p.transport.value() == ('GET http://www.example.faketld/ HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n') @pytest.inlineCallbacks def test_proxy_upstream_client_connection_made_creds(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345, 'username':'foo', 'password':'password'}) r = http.Request(('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n')) p = yield gen_client_protocol(r, save_all=False) assert isinstance(p, UpstreamHTTPProxyClient) assert p.transport.value() == ('GET http://www.example.faketld/ HTTP/1.1\r\n' 'Host: www.example.faketld\r\n' 'Proxy-Authorization: Basic %s\r\n\r\n') % base64.b64encode('foo:password') @pytest.inlineCallbacks def test_proxy_upstream_client_connection_made_ssl(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345}) r = http.Request(('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n')) r.is_ssl = True p = yield gen_client_protocol(r, save_all=False) assert isinstance(p, UpstreamHTTPProxyClient) assert p.transport.value() == ('CONNECT www.example.faketld:443 HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n') @pytest.inlineCallbacks def test_proxy_upstream_client_connection_made_ssl_creds(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345, 'username':'foo', 'password':'password'}) r = http.Request(('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n')) r.is_ssl = True p = yield gen_client_protocol(r, save_all=False) assert isinstance(p, UpstreamHTTPProxyClient) assert p.transport.value() == ('CONNECT www.example.faketld:443 HTTP/1.1\r\n' 'Host: www.example.faketld\r\n' 'Proxy-Authorization: Basic %s\r\n\r\n') % base64.b64encode('foo:password') @pytest.inlineCallbacks def test_proxy_upstream_client_connection_made_ssl(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345}) mstarttls = mocker.patch('pappyproxy.tests.testutil.TLSStringTransport.startTLS') r = http.Request(('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n')) r.is_ssl = True p = yield gen_client_protocol(r, save_all=False) assert p.transport.value() == ('CONNECT www.example.faketld:443 HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n') assert not mstarttls.called p.transport.clear() p.dataReceived('HTTP/1.1 200 OK\r\n\r\n') assert mstarttls.called assert p.transport.value() == ('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n') @pytest.inlineCallbacks def test_proxy_upstream_client_connection_made_ssl_creds(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345, 'username':'foo', 'password':'password'}) mstarttls = mocker.patch('pappyproxy.tests.testutil.TLSStringTransport.startTLS') r = http.Request(('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n')) r.is_ssl = True p = yield gen_client_protocol(r, save_all=False) assert p.transport.value() == ('CONNECT www.example.faketld:443 HTTP/1.1\r\n' 'Host: www.example.faketld\r\n' 'Proxy-Authorization: Basic %s\r\n\r\n') % base64.b64encode('foo:password') assert not mstarttls.called p.transport.clear() p.dataReceived('HTTP/1.1 200 OK\r\n\r\n') assert mstarttls.called assert p.transport.value() == ('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n') @pytest.inlineCallbacks def test_proxy_upstream_client_connection_incorrect_creds(mocker): http_proxy_config(mocker, {'host': '12345', 'port': 12345, 'username':'foo', 'password':'password'}) mstarttls = mocker.patch('pappyproxy.tests.testutil.TLSStringTransport.startTLS') closed = mocker.patch('pappyproxy.tests.testutil.TLSStringTransport.loseConnection') r = http.Request(('GET / HTTP/1.1\r\n' 'Host: www.example.faketld\r\n\r\n')) r.is_ssl = True p = yield gen_client_protocol(r, save_all=False) assert p.transport.value() == ('CONNECT www.example.faketld:443 HTTP/1.1\r\n' 'Host: www.example.faketld\r\n' 'Proxy-Authorization: Basic %s\r\n\r\n') % base64.b64encode('foo:password') p.transport.clear() p.dataReceived('HTTP/1.1 407 YOU DUN FUCKED UP\r\n\r\n') assert not mstarttls.called assert p.transport.value() == '' assert closed.called ### ProxyClient tests @pytest.inlineCallbacks def test_proxy_client_simple(mocker): rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) req = http.Request('GET / HTTP/1.1\r\n\r\n') client = yield gen_client_protocol(req, stream_response=False) assert client.transport.value() == 'GET / HTTP/1.1\r\n\r\n' client.transport.clear() rsp = 'HTTP/1.1 200 OKILE DOKELY\r\n\r\n' client.dataReceived(rsp) retpair = yield client.data_defer assert retpair.response.full_message == rsp @pytest.inlineCallbacks def test_proxy_client_stream(mocker): rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) req = http.Request('GET / HTTP/1.1\r\n\r\n') client = yield gen_client_protocol(req, stream_response=True) client.transport.clear() client.dataReceived('HTTP/1.1 404 GET FUCKE') assert client.factory.return_transport.value() == 'HTTP/1.1 404 GET FUCKE' client.factory.return_transport.clear() client.dataReceived('D ASSHOLE\r\nContent-Length: 4\r\n\r\nABCD') assert client.factory.return_transport.value() == 'D ASSHOLE\r\nContent-Length: 4\r\n\r\nABCD' retpair = yield client.data_defer assert retpair.response.full_message == 'HTTP/1.1 404 GET FUCKED ASSHOLE\r\nContent-Length: 4\r\n\r\nABCD' @pytest.inlineCallbacks def test_proxy_client_nostream(mocker): rsave = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) req = http.Request('GET / HTTP/1.1\r\n\r\n') client = yield gen_client_protocol(req, stream_response=False) client.transport.clear() client.dataReceived('HTTP/1.1 404 GET FUCKE') assert client.factory.return_transport.value() == '' client.factory.return_transport.clear() client.dataReceived('D ASSHOLE\r\nContent-Length: 4\r\n\r\nABCD') assert client.factory.return_transport.value() == '' retpair = yield client.data_defer assert retpair.response.full_message == 'HTTP/1.1 404 GET FUCKED ASSHOLE\r\nContent-Length: 4\r\n\r\nABCD'