Version 0.2.7

This commit is contained in:
Rob Glew 2016-02-18 15:29:43 -06:00
parent fda0166e72
commit f4274e1e82
27 changed files with 2136 additions and 493 deletions

View file

@ -0,0 +1,65 @@
import pytest
import string
import mock
from collections import OrderedDict
from testutil import mock_deferred, func_deleted, TLSStringTransport, freeze, mock_int_macro, no_tcp
from pappyproxy.http import Request, Response
from pappyproxy import macros
class CloudToButtMacro(macros.InterceptMacro):
def __init__(self):
macros.InterceptMacro.__init__(self)
self.intercept_requests = True
self.intercept_responses = True
def mangle_request(self, request):
return Request(string.replace(request.full_message, 'cloud', 'butt'))
def mangle_response(self, response):
return Response(string.replace(response.full_message, 'cloud', 'butt'))
@pytest.fixture
def httprequest():
return Request(('POST /test-request HTTP/1.1\r\n'
'Content-Length: 4\r\n'
'\r\n'
'AAAA'))
@pytest.inlineCallbacks
def test_mangle_request_simple(httprequest):
orig_req = httprequest.copy() # in case it gets mangled
(new_req, mangled) = yield macros.mangle_request(orig_req, {})
assert new_req == orig_req
assert httprequest == orig_req
assert not mangled
@pytest.inlineCallbacks
def test_mangle_request_single(httprequest):
orig_req = httprequest.copy() # in case it gets mangled
macro = mock_int_macro(modified_req=('GET /modified HTTP/1.1\r\n\r\n'))
expected_req = Request('GET /modified HTTP/1.1\r\n\r\n')
(new_req, mangled) = yield macros.mangle_request(orig_req, {'testmacro': macro})
assert new_req == expected_req
assert httprequest == orig_req
assert httprequest.unmangled is None
assert new_req.unmangled == orig_req
assert mangled
@pytest.inlineCallbacks
def test_mangle_request_multiple(httprequest):
orig_req = httprequest.copy() # in case it gets mangled
macro = mock_int_macro(modified_req=('GET /cloud HTTP/1.1\r\n\r\n'))
macro2 = CloudToButtMacro()
intmacros = OrderedDict()
intmacros['testmacro'] = macro
intmacros['testmacro2'] = macro2
(new_req, mangled) = yield macros.mangle_request(orig_req, intmacros)
expected_req = Request('GET /butt HTTP/1.1\r\n\r\n')
assert new_req == expected_req
assert httprequest == orig_req
assert httprequest.unmangled is None
assert new_req.unmangled == orig_req
assert mangled

View file

@ -1,82 +1,56 @@
import os
import pytest
import mock
import twisted.internet
import twisted.test
import random
import datetime
import pappyproxy
from pappyproxy import http
from pappyproxy import macros
from pappyproxy import config
from pappyproxy.proxy import ProxyClient, ProxyClientFactory, ProxyServerFactory
from testutil import mock_deferred, func_deleted, func_ignored_deferred, func_ignored, no_tcp
from twisted.internet.protocol import ServerFactory
from twisted.test.iosim import FakeTransport
from twisted.internet import defer, reactor
from pappyproxy.proxy import ProxyClientFactory, ProxyServerFactory
from testutil import mock_deferred, func_deleted, TLSStringTransport, freeze, mock_int_macro, no_tcp
####################
## Fixtures
MANGLED_REQ = 'GET /mangled HTTP/1.1\r\n\r\n'
MANGLED_RSP = 'HTTP/1.1 500 MANGLED\r\nContent-Length: 0\r\n\r\n'
@pytest.fixture
def unconnected_proxyserver(mocker):
mocker.patch("twisted.test.iosim.FakeTransport.startTLS")
mocker.patch("pappyproxy.proxy.load_certs_from_dir", new=mock_generate_cert)
factory = ProxyServerFactory()
protocol = factory.buildProtocol(('127.0.0.1', 0))
protocol.makeConnection(FakeTransport(protocol, True))
return protocol
@pytest.fixture
def proxyserver(mocker):
mocker.patch("twisted.test.iosim.FakeTransport.startTLS")
mocker.patch("pappyproxy.proxy.load_certs_from_dir", new=mock_generate_cert)
factory = ProxyServerFactory()
protocol = factory.buildProtocol(('127.0.0.1', 0))
protocol.makeConnection(FakeTransport(protocol, True))
protocol.lineReceived('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1')
protocol.lineReceived('')
protocol.transport.getOutBuffer()
return protocol
@pytest.fixture
def proxy_connection():
@defer.inlineCallbacks
def gen_connection(send_data, new_req=False, new_rsp=False,
drop_req=False, drop_rsp=False):
factory = ProxyClientFactory(http.Request(send_data))
macro = gen_mangle_macro(new_req, new_rsp, drop_req, drop_rsp)
factory.intercepting_macros['pappy_mangle'] = macro
protocol = factory.buildProtocol(None)
tr = FakeTransport(protocol, True)
protocol.makeConnection(tr)
sent = yield protocol.data_defer
print sent
defer.returnValue((protocol, sent, factory.data_defer))
return gen_connection
@pytest.fixture
def in_scope_true(mocker):
new_in_scope = mock.MagicMock()
new_in_scope.return_value = True
mocker.patch("pappyproxy.context.in_scope", new=new_in_scope)
return new_in_scope
@pytest.fixture
def in_scope_false(mocker):
new_in_scope = mock.MagicMock()
new_in_scope.return_value = False
mocker.patch("pappyproxy.context.in_scope", new=new_in_scope)
return new_in_scope
## Autorun fixtures
@pytest.fixture(autouse=True)
def ignore_save(mocker):
mocker.patch("pappyproxy.http.Request.async_deep_save", func_ignored_deferred)
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()
def socks_config(mocker, config):
mocker.patch('pappyproxy.config.SOCKS_PROXY', new=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
def gen_client_protocol(req, stream_response=False):
return_transport = TLSStringTransport()
factory = ProxyClientFactory(req,
save_all=True,
stream_response=stream_response,
return_transport=return_transport)
protocol = factory.buildProtocol(('127.0.0.1', 0), _do_callback=False)
tr = TLSStringTransport()
protocol.makeConnection(tr)
return 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()
####################
## Mock functions
@ -134,151 +108,522 @@ def mock_generate_cert(cert_dir):
'-----END CERTIFICATE-----')
return (ca_key, private_key)
def gen_mangle_macro(modified_req=None, modified_rsp=None,
drop_req=False, drop_rsp=False):
macro = mock.MagicMock()
if modified_req or drop_req:
macro.async_req = True
macro.intercept_requests = True
if drop_req:
newreq = None
else:
newreq = http.Request(modified_req)
macro.async_mangle_request.return_value = mock_deferred(newreq)
else:
macro.intercept_requests = False
########
## Tests
if modified_rsp or drop_rsp:
macro.async_rsp = True
macro.intercept_responses = True
if drop_rsp:
newrsp = None
else:
newrsp = http.Response(modified_rsp)
macro.async_mangle_response.return_value = mock_deferred(newrsp)
else:
macro.intercept_responses = False
return macro
def notouch_mangle_req(request):
d = mock_deferred(request)
return d
def notouch_mangle_rsp(request):
d = mock_deferred(request.response)
return d
def req_mangler_change(request):
req = http.Request('GET /mangled HTTP/1.1\r\n\r\n')
d = mock_deferred(req)
return d
def rsp_mangler_change(request):
rsp = http.Response('HTTP/1.1 500 MANGLED\r\n\r\n')
d = mock_deferred(rsp)
return d
def req_mangler_drop(request):
return mock_deferred(None)
def rsp_mangler_drop(request):
return mock_deferred(None)
####################
## Unit test tests
def test_proxy_server_fixture(unconnected_proxyserver):
unconnected_proxyserver.transport.write('hello')
assert unconnected_proxyserver.transport.getOutBuffer() == 'hello'
@pytest.inlineCallbacks
def test_mock_deferreds():
d = mock_deferred('Hello!')
r = yield d
assert r == 'Hello!'
def test_deleted():
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):
reactor.connectTCP("www.google.com", "80", ServerFactory)
SSL4ClientEndpoint('aasdfasdf.sdfwerqwer')
with pytest.raises(NotImplementedError):
reactor.connectSSL("www.google.com", "80", ServerFactory)
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
####################
## Proxy Server Tests
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
def test_proxy_server_connect(unconnected_proxyserver, mocker, in_scope_true):
mocker.patch("twisted.internet.reactor.connectSSL")
unconnected_proxyserver.lineReceived('CONNECT https://www.dddddd.fff:433 HTTP/1.1')
unconnected_proxyserver.lineReceived('')
assert unconnected_proxyserver.transport.getOutBuffer() == 'HTTP/1.1 200 Connection established\r\n\r\n'
assert unconnected_proxyserver._request_obj.is_ssl
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
def test_proxy_server_basic(proxyserver, mocker, in_scope_true):
mocker.patch("twisted.internet.reactor.connectSSL")
mocker.patch('pappyproxy.proxy.ProxyServer.setRawMode')
proxyserver.lineReceived('GET / HTTP/1.1')
proxyserver.lineReceived('')
## ProxyServer._make_remote_connection
assert proxyserver.setRawMode.called
args, kwargs = twisted.internet.reactor.connectSSL.call_args
assert args[0] == 'www.AAAA.BBBB'
assert args[1] == 443
@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")
@pytest.inlineCallbacks
def test_proxy_client_nomangle(mocker, proxy_connection, in_scope_true):
# Make the connection
(prot, sent, retreq_deferred) = \
yield proxy_connection('GET / HTTP/1.1\r\n\r\n', None, None)
assert sent.full_request == 'GET / HTTP/1.1\r\n\r\n'
prot.lineReceived('HTTP/1.1 200 OK')
prot.lineReceived('Content-Length: 0')
prot.lineReceived('')
ret_req = yield retreq_deferred
response = ret_req.response.full_response
assert response == 'HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n'
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_client_mangle_req(mocker, proxy_connection, in_scope_true):
# Make the connection
(prot, sent, retreq_deferred) = \
yield proxy_connection('GET / HTTP/1.1\r\n\r\n', MANGLED_REQ, None)
assert sent.full_request == 'GET /mangled HTTP/1.1\r\n\r\n'
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_client_mangle_rsp(mocker, proxy_connection, in_scope_true):
# Make the connection
(prot, sent, retreq_deferred) = \
yield proxy_connection('GET / HTTP/1.1\r\n\r\n', None, MANGLED_RSP)
prot.lineReceived('HTTP/1.1 200 OK')
prot.lineReceived('Content-Length: 0')
prot.lineReceived('')
req = yield retreq_deferred
response = req.response.full_response
assert response == 'HTTP/1.1 500 MANGLED\r\nContent-Length: 0\r\n\r\n'
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_drop_req(mocker, proxy_connection, in_scope_true):
(prot, sent, retreq_deferred) = \
yield proxy_connection('GET / HTTP/1.1\r\n\r\n', None, None, True, False)
assert sent is None
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_drop_rsp(mocker, proxy_connection, in_scope_true):
(prot, sent, retreq_deferred) = \
yield proxy_connection('GET / HTTP/1.1\r\n\r\n', None, None, False, True)
prot.lineReceived('HTTP/1.1 200 OK')
prot.lineReceived('Content-Length: 0')
prot.lineReceived('')
retreq = yield retreq_deferred
assert retreq.response is None
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_360_noscope(mocker, proxy_connection, in_scope_false):
# Make the connection
(prot, sent, retreq_deferred) = yield proxy_connection('GET / HTTP/1.1\r\n\r\n')
assert sent.full_request == 'GET / HTTP/1.1\r\n\r\n'
prot.lineReceived('HTTP/1.1 200 OK')
prot.lineReceived('Content-Length: 0')
prot.lineReceived('')
req = yield retreq_deferred
assert req.response.full_response == 'HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n'
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_prepare_mangle_rsp(mocker, freeze):
# 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
# mocker.patch('pappyproxy.macros.mangle_response').return_value = (req, False)
# cf = ProxyClientFactory(req,
# save_all=False,
# stream_response=False,
# return_transport=None)
# result = yield cf.return_request_pair(req)
# assert result == req
# assert req.time_start == datetime.datetime(2015, 1, 1, 3, 30, 15, 50)
# assert len(rsave.mock_calls) == 0
### 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 = 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 = 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 = 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'

View file

@ -3,12 +3,19 @@ import mock
import pytest
import StringIO
from twisted.internet import defer
from twisted.test.proto_helpers import StringTransport
from pappyproxy import http
next_mock_id = 0
class ClassDeleted():
pass
class TLSStringTransport(StringTransport):
def startTLS(self, context, factory):
pass
def func_deleted(*args, **kwargs):
raise NotImplementedError()
@ -18,7 +25,7 @@ def func_ignored(*args, **kwargs):
def func_ignored_deferred(*args, **kwargs):
return mock_deferred(None)
def mock_deferred(value):
def mock_deferred(value=None):
# Generates a function that can be used to make a deferred that can be used
# to mock out deferred-returning responses
def g(data):
@ -33,6 +40,10 @@ def no_tcp(mocker):
# Don't make tcp connections
mocker.patch("twisted.internet.reactor.connectTCP", new=func_deleted)
mocker.patch("twisted.internet.reactor.connectSSL", new=func_deleted)
mocker.patch("twisted.internet.endpoints.SSL4ClientEndpoint", new=func_deleted)
mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint", new=func_deleted)
mocker.patch("txsocksx.client.SOCKS5ClientEndpoint", new=func_deleted)
mocker.patch("txsocksx.tls.TLSWrapClientEndpoint", new=func_deleted)
@pytest.fixture
def ignore_tcp(mocker):
@ -73,3 +84,71 @@ def mock_deep_save(mocker, fake_saving):
def print_fuck(*args, **kwargs):
print 'fuck'
@pytest.fixture
def freeze(monkeypatch):
""" Now() manager patches datetime return a fixed, settable, value
(freezes time)
stolen from http://stackoverflow.com/a/28073449
"""
import datetime
original = datetime.datetime
class FreezeMeta(type):
def __instancecheck__(self, instance):
if type(instance) == original or type(instance) == Freeze:
return True
class Freeze(datetime.datetime):
__metaclass__ = FreezeMeta
@classmethod
def freeze(cls, val, utcval=None):
cls.utcfrozen = utcval
cls.frozen = val
@classmethod
def now(cls):
return cls.frozen
@classmethod
def utcnow(cls):
# added since requests use utcnow
return cls.utcfrozen or cls.frozen
@classmethod
def delta(cls, timedelta=None, **kwargs):
""" Moves time fwd/bwd by the delta"""
from datetime import timedelta as td
if not timedelta:
timedelta = td(**kwargs)
cls.frozen += timedelta
monkeypatch.setattr(datetime, 'datetime', Freeze)
Freeze.freeze(original.now())
return Freeze
def mock_int_macro(modified_req=None, modified_rsp=None,
drop_req=False, drop_rsp=False):
macro = mock.MagicMock()
if modified_req or drop_req:
macro.async_req = True
macro.intercept_requests = True
if drop_req:
newreq = None
else:
newreq = http.Request(modified_req)
macro.async_mangle_request.return_value = mock_deferred(newreq)
else:
macro.intercept_requests = False
if modified_rsp or drop_rsp:
macro.async_rsp = True
macro.intercept_responses = True
if drop_rsp:
newrsp = None
else:
newrsp = http.Response(modified_rsp)
macro.async_mangle_response.return_value = mock_deferred(newrsp)
else:
macro.intercept_responses = False
return macro