parent
6633423420
commit
4e6801e4d8
39 changed files with 917 additions and 443 deletions
@ -0,0 +1,3 @@ |
||||
include README.md |
||||
include LICENSE.txt |
||||
recursive-include pappyproxy *.py *.vim |
@ -1,3 +0,0 @@ |
||||
[run] |
||||
omit = tests/*, schema/* |
||||
|
@ -1,22 +0,0 @@ |
||||
-----BEGIN CERTIFICATE----- |
||||
MIIDjzCCAncCFQDmrLdMg37vTWXeF9Zp0WjQmQWF1jANBgkqhkiG9w0BAQsFADBg |
||||
MQswCQYDVQQGEwJVUzERMA8GA1UECAwITWljaGlnYW4xEjAQBgNVBAcMCUFubiBB |
||||
cmJvcjEUMBIGA1UECgwLUGFwcHkgUHJveHkxFDASBgNVBAMMC1BhcHB5IFByb3h5 |
||||
MB4XDTE1MTAyNjE2MDYxMVoXDTI1MTAyMzE2MDYxMVowYDELMAkGA1UEBhMCVVMx |
||||
ETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxFDASBgNVBAoM |
||||
C1BhcHB5IFByb3h5MRQwEgYDVQQDDAtQYXBweSBQcm94eTCCASIwDQYJKoZIhvcN |
||||
AQEBBQADggEPADCCAQoCggEBAPNQo64jLgvKVKNqqLi0cDBfWqp+ZhEDaGdm3Rjl |
||||
AFerqmDHyAeCu1GENQAwcmmeXCwMYSbjcMHSrExR+rcQRxvJ8OOp2doP43+T9hd8 |
||||
rZt+PPOiBVG0cUrfdsVdbUyGjPmZFtWaiSVG2gUOdO2m7jK5WwIEcW5u6vEfmgco |
||||
/JLvtdgGZGIlsZGeQGcJdeZ6LaPKLHxPAkgRQduQTpK5nKiFi0Aqj4AsqddcZ4fo |
||||
X3zGsypkt0NVTn4nMZLR9Ml5mwzTltr9BBtSVqMIMwqVkKLkGFdaIFsY5dK3UYUV |
||||
vqLGB6ubheULLjmkv9FJLmaHfnLb2jjA17K+y3QKosMVldcCAwEAAaNFMEMwEgYD |
||||
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNo5o+5e |
||||
a0sNMlW/75VgGJCv2AcJMA0GCSqGSIb3DQEBCwUAA4IBAQBMbpA8XkEvtpErHsy/ |
||||
FCtzQGmn88idU43fFSi0bcsWWc1ekapd7iTramItvZ8OCZD3/oVE4VIwumuJuoVk |
||||
OU/Tip0e+haPV5f1JImdsk2f20WJ0lJ5CyrrRcddqgVrcQbB8DwaJSJRXzrSD9Cp |
||||
UDfJhIh2zxRolGql29X6QiFukV3CIHn2hF+QYlMrxkoI0e4r6sDtmN4/VccgADdH |
||||
pQeVz4z/ZxKBIh7Xol8K6Qr+gXnlkbp3n5WXGHbv4YsK995z9yVZpuLPUHbpnSzr |
||||
KVJ5I4joA22uc2tqeKvfp4QsE8fa/nVNRv/LZZeCdg0zrXXpE9RoxNirwEcQwAo1 |
||||
x25g |
||||
-----END CERTIFICATE----- |
@ -1,28 +0,0 @@ |
||||
-----BEGIN PRIVATE KEY----- |
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDzUKOuIy4LylSj |
||||
aqi4tHAwX1qqfmYRA2hnZt0Y5QBXq6pgx8gHgrtRhDUAMHJpnlwsDGEm43DB0qxM |
||||
Ufq3EEcbyfDjqdnaD+N/k/YXfK2bfjzzogVRtHFK33bFXW1Mhoz5mRbVmoklRtoF |
||||
DnTtpu4yuVsCBHFuburxH5oHKPyS77XYBmRiJbGRnkBnCXXmei2jyix8TwJIEUHb |
||||
kE6SuZyohYtAKo+ALKnXXGeH6F98xrMqZLdDVU5+JzGS0fTJeZsM05ba/QQbUlaj |
||||
CDMKlZCi5BhXWiBbGOXSt1GFFb6ixgerm4XlCy45pL/RSS5mh35y29o4wNeyvst0 |
||||
CqLDFZXXAgMBAAECggEBAJxlD+ClkjpX4lFsBGk86gPdtrxyJI74/snAD4up3q97 |
||||
kzdEEuno+Rhrf1nQyinjdWGGz4ecl+St0rv30cyLdPmCswjTK0mD/voJFByCsmCJ |
||||
IwqC8SJUdqHmw0QXSmLu9XyWD1xbSZ4hTZAEe9op+1+1Tq8cRgDy4Kb+ZhYGHVsf |
||||
4o1RFGBCtSGLFBC908xZnQlqzGHtCuiBecJiWqoFK+mm3TgEUp4VDPRSPsWDWYnJ |
||||
KxciTSE9roBF7VAe5ocTRdn+tj9GVaNaBLqb1XhkU41wZxVMoid0OVgxkmyEdAyR |
||||
lL1/zVyQDgJbke4t6dgu4NCAoPWXKZP1zxNa1Ied51kCgYEA+h2X7MO8rYyWHGT7 |
||||
EZoPpHSrR3F1MnsRgXnkVt5dSrwAQlLmQmmWnjVtEQM72Eox1Czdz+GjILpvfwNF |
||||
fktzDa1GghO5TdDibcchG01qLeqEj0vgvtCP1YFLeCBZJv4yPxpaHWhyUOYPWoXq |
||||
Mze7yYbkh2uYORPKgu+N4b4oH90CgYEA+QoWQ+44j2jld4DLvYpW/tf2kvKkmFl5 |
||||
43KSVXkDHSnEfO+RFpFQ8rCOKetlMbcuQMakTz++fh3smHWGZ/S1Hm1ZUIRQqCzq |
||||
m1dTg8PX6pH9e7/0gebFqQWtGhWQdnSWmGZAEnAnmFq6DrDB0FHvfS+VePC1knEJ |
||||
/Aw4l+YFy0MCgYA60YLM1ysj1Q/oFYdFmGldT2KIJpJdELwJKtUb6Kcf0B5vendT |
||||
3ujgw8emXJBSSQB22SZAoNtv8ugNgoNxM+UWrk0KggDt39Wf41hRx17U9XW/DSUJ |
||||
OprYptNMqK7OkLDYTiYrDEj15WRu8VcmPFEZD3PmtNLTeWgCart+/u0IsQKBgQCG |
||||
xSirdl1xbmjPtQmM9zKBE0pC18CvGazWo4gBbU18GMBWhCbWOam+zEEC+np23xTO |
||||
xTDiGjLyeSsyjldAJrNlVfPBmPk1KamEi0uMwQ01ye+NaqHdMo/BGmtE9GqLUCi3 |
||||
LI576+nhjyelD46zN8QM0RVor4rzRu0KU2rE+RwllQKBgQDZ1j5Uhblxn+WJ1/z3 |
||||
xZfP23VJLVCCvBIXaHENCl01/9hSBFqH0K+EUUfeJesWoh7KSdaiHXGRR1XdB1rs |
||||
Bmzh4wPgIlcc8CPmJxZ09fM2ggHSZf1baV8lEf64/N3OnENDvUAepzwIe0IhKs1i |
||||
pzpCgCGttWxEZJvcug4AOulfQA== |
||||
-----END PRIVATE KEY----- |
@ -1,51 +0,0 @@ |
||||
import imp |
||||
import json |
||||
import os |
||||
import shutil |
||||
|
||||
# Make sure we have a config file |
||||
if not os.path.isfile('./config.json'): |
||||
print "Copying default config to directory" |
||||
default_config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), |
||||
'default_user_config.json') |
||||
shutil.copyfile(default_config_file, './config.json') |
||||
|
||||
# Load local project config |
||||
with open('./config.json', 'r') as f: |
||||
proj_config = json.load(f) |
||||
|
||||
# Substitution dictionary |
||||
subs = {} |
||||
subs['PAPPYDIR'] = os.path.dirname(os.path.realpath(__file__)) |
||||
|
||||
# Data file settings |
||||
if 'data_file' in proj_config: |
||||
DATAFILE = proj_config["data_file"].format(**subs) |
||||
else: |
||||
DATAFILE = 'data.db' |
||||
|
||||
# Debug settings |
||||
if 'debug_dir' in proj_config: |
||||
DEBUG_TO_FILE = True |
||||
DEBUG_DIR = proj_config["debug_dir"].format(**subs) |
||||
else: |
||||
DEBUG_DIR = None |
||||
DEBUG_TO_FILE = False |
||||
DEBUG_VERBOSITY = 0 |
||||
|
||||
# Cert directory settings |
||||
if 'cert_dir' in proj_config: |
||||
CERT_DIR = proj_config["cert_dir"].format(**subs) |
||||
else: |
||||
CERT_DIR = './certs' |
||||
SSL_PKEY_FILE = 'private.key' |
||||
SSL_CA_FILE = 'certificate.crt' |
||||
|
||||
# Listener settings |
||||
if "proxy_listeners" in proj_config: |
||||
LISTENERS = [] |
||||
for l in proj_config["proxy_listeners"]: |
||||
LISTENERS.append((l['port'], l['interface'])) |
||||
else: |
||||
LISTENERS = [(8000, '127.0.0.1')] |
||||
|
@ -1,56 +0,0 @@ |
||||
import pytest |
||||
import mangle |
||||
import twisted.internet |
||||
import twisted.test |
||||
|
||||
from proxy import ProxyClient, ProxyClientFactory, ProxyServer |
||||
from testutil import mock_deferred, func_deleted, no_tcp, ignore_tcp, no_database, func_ignored |
||||
from twisted.internet.protocol import ServerFactory |
||||
from twisted.test.iosim import FakeTransport |
||||
from twisted.internet import defer, reactor |
||||
|
||||
#################### |
||||
## Fixtures |
||||
|
||||
@pytest.fixture |
||||
def proxyserver(monkeypatch): |
||||
monkeypatch.setattr("twisted.test.iosim.FakeTransport.startTLS", func_ignored) |
||||
factory = ServerFactory() |
||||
factory.protocol = ProxyServer |
||||
protocol = factory.buildProtocol(('127.0.0.1', 0)) |
||||
protocol.makeConnection(FakeTransport(protocol, True)) |
||||
return protocol |
||||
|
||||
## Autorun fixtures |
||||
|
||||
@pytest.fixture(autouse=True) |
||||
def no_mangle(monkeypatch): |
||||
# Don't call anything in mangle.py |
||||
monkeypatch.setattr("mangle.mangle_request", func_deleted) |
||||
monkeypatch.setattr("mangle.mangle_response", func_deleted) |
||||
|
||||
#################### |
||||
## Unit test tests |
||||
|
||||
def test_proxy_server_fixture(proxyserver): |
||||
proxyserver.transport.write('hello') |
||||
assert proxyserver.transport.getOutBuffer() == 'hello' |
||||
|
||||
@pytest.inlineCallbacks |
||||
def test_mock_deferreds(mock_deferred): |
||||
d = mock_deferred('Hello!') |
||||
r = yield d |
||||
assert r == 'Hello!' |
||||
|
||||
def test_deleted(): |
||||
with pytest.raises(NotImplementedError): |
||||
reactor.connectTCP("www.google.com", "80", ServerFactory) |
||||
|
||||
#################### |
||||
## Proxy Server Tests |
||||
|
||||
def test_proxy_server_connect(proxyserver): |
||||
proxyserver.lineReceived('CONNECT www.dddddd.fff:433 HTTP/1.1') |
||||
proxyserver.lineReceived('') |
||||
assert proxyserver.transport.getOutBuffer() == 'HTTP/1.1 200 Connection established\r\n\r\n' |
||||
#assert starttls got called |
@ -1,42 +0,0 @@ |
||||
import pytest |
||||
from twisted.internet import defer |
||||
|
||||
class ClassDeleted(): |
||||
pass |
||||
|
||||
def func_deleted(*args, **kwargs): |
||||
raise NotImplementedError() |
||||
|
||||
def func_ignored(*args, **kwargs): |
||||
pass |
||||
|
||||
@pytest.fixture |
||||
def mock_deferred(): |
||||
# Generates a function that can be used to make a deferred that can be used |
||||
# to mock out deferred-returning responses |
||||
def f(value): |
||||
def g(data): |
||||
return value |
||||
d = defer.Deferred() |
||||
d.addCallback(g) |
||||
d.callback(None) |
||||
return d |
||||
return f |
||||
|
||||
@pytest.fixture(autouse=True) |
||||
def no_tcp(monkeypatch): |
||||
# Don't make tcp connections |
||||
monkeypatch.setattr("twisted.internet.reactor.connectTCP", func_deleted) |
||||
monkeypatch.setattr("twisted.internet.reactor.connectSSL", func_deleted) |
||||
|
||||
@pytest.fixture |
||||
def ignore_tcp(monkeypatch): |
||||
# Don't make tcp connections |
||||
monkeypatch.setattr("twisted.internet.reactor.connectTCP", func_ignored) |
||||
monkeypatch.setattr("twisted.internet.reactor.connectSSL", func_ignored) |
||||
|
||||
@pytest.fixture(autouse=True) |
||||
def no_database(monkeypatch): |
||||
# Don't make database queries |
||||
monkeypatch.setattr("twisted.enterprise.adbapi.ConnectionPool", |
||||
ClassDeleted) |
@ -1 +0,0 @@ |
||||
glew@localhost.787:1446907770 |
@ -0,0 +1,3 @@ |
||||
[run] |
||||
omit = tests/*, schema/*, console.py, vim_repeater/* |
||||
|
@ -1,6 +1,6 @@ |
||||
|
||||
install-third-party: |
||||
pip install -r requirements.txt
|
||||
pip install -e ..
|
||||
|
||||
test: |
||||
py.test -rw --twisted --cov-config .coveragerc --cov=. tests/
|
@ -0,0 +1,71 @@ |
||||
import imp |
||||
import json |
||||
import os |
||||
import shutil |
||||
|
||||
PAPPY_DIR = os.path.dirname(os.path.realpath(__file__)) |
||||
|
||||
CERT_DIR = PAPPY_DIR |
||||
DATAFILE = 'data.db' |
||||
DEBUG_DIR = None |
||||
DEBUG_TO_FILE = False |
||||
DEBUG_VERBOSITY = 0 |
||||
LISTENERS = [(8000, '127.0.0.1')] |
||||
SSL_CA_FILE = 'certificate.crt' |
||||
SSL_PKEY_FILE = 'private.key' |
||||
|
||||
def get_default_config(): |
||||
default_config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), |
||||
'default_user_config.json') |
||||
with open(default_config_file) as f: |
||||
settings = json.load(f) |
||||
return settings |
||||
|
||||
def load_settings(proj_config): |
||||
global CERT_DIR |
||||
global DATAFILE |
||||
global DEBUG_DIR |
||||
global DEBUG_TO_FILE |
||||
global DEBUG_VERBOSITY |
||||
global LISTENERS |
||||
global PAPPY_DIR |
||||
global SSL_CA_FILE |
||||
global SSL_PKEY_FILE |
||||
|
||||
# Substitution dictionary |
||||
subs = {} |
||||
subs['PAPPYDIR'] = PAPPY_DIR |
||||
|
||||
# Data file settings |
||||
if 'data_file' in proj_config: |
||||
DATAFILE = proj_config["data_file"].format(**subs) |
||||
|
||||
# Debug settings |
||||
if 'debug_dir' in proj_config: |
||||
if proj_config['debug_dir']: |
||||
DEBUG_TO_FILE = True |
||||
DEBUG_DIR = proj_config["debug_dir"].format(**subs) |
||||
|
||||
# Cert directory settings |
||||
if 'cert_dir' in proj_config: |
||||
CERT_DIR = proj_config["cert_dir"].format(**subs) |
||||
|
||||
# Listener settings |
||||
if "proxy_listeners" in proj_config: |
||||
LISTENERS = [] |
||||
for l in proj_config["proxy_listeners"]: |
||||
LISTENERS.append((l['port'], l['interface'])) |
||||
|
||||
|
||||
def load_from_file(fname): |
||||
# Make sure we have a config file |
||||
if not os.path.isfile(fname): |
||||
print "Copying default config to %s" % fname |
||||
default_config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), |
||||
'default_user_config.json') |
||||
shutil.copyfile(default_config_file, fname) |
||||
|
||||
# Load local project config |
||||
with open(fname, 'r') as f: |
||||
proj_config = json.load(f) |
||||
load_settings(proj_config) |
@ -1,7 +1,8 @@ |
||||
import comm |
||||
import subprocess |
||||
import os |
||||
|
||||
from pappyproxy import comm |
||||
|
||||
def start_editor(reqid): |
||||
script_loc = os.path.join(os.path.dirname(__file__), "vim_repeater", "repeater.vim") |
||||
#print "RepeaterSetup %d %d"%(reqid, comm_port) |
@ -1,4 +1,4 @@ |
||||
import http |
||||
from pappyproxy import http |
||||
from twisted.internet import defer |
||||
|
||||
""" |
@ -1,7 +1,7 @@ |
||||
import pytest |
||||
|
||||
import context |
||||
from http import Request, Response, ResponseCookie |
||||
from pappyproxy import context |
||||
from pappyproxy.http import Request, Response, ResponseCookie |
||||
|
||||
@pytest.fixture |
||||
def http_request(): |
@ -0,0 +1,220 @@ |
||||
import pytest |
||||
import mock |
||||
import twisted.internet |
||||
import twisted.test |
||||
|
||||
from pappyproxy import http |
||||
from pappyproxy import mangle |
||||
from pappyproxy.proxy import ProxyClient, ProxyClientFactory, ProxyServer |
||||
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 |
||||
|
||||
#################### |
||||
## Fixtures |
||||
|
||||
@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 = ServerFactory() |
||||
factory.protocol = ProxyServer |
||||
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 = ServerFactory() |
||||
factory.protocol = ProxyServer |
||||
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): |
||||
factory = ProxyClientFactory(http.Request(send_data)) |
||||
protocol = factory.buildProtocol(None) |
||||
tr = FakeTransport(protocol, True) |
||||
protocol.makeConnection(tr) |
||||
sent = yield protocol.data_defer |
||||
defer.returnValue((protocol, sent, factory.data_defer)) |
||||
return gen_connection |
||||
|
||||
## Autorun fixtures |
||||
|
||||
# @pytest.fixture(autouse=True) |
||||
# def no_mangle(mocker): |
||||
# # Don't call anything in mangle.py |
||||
# mocker.patch("mangle.mangle_request", notouch_mangle_req) |
||||
# mocker.patch("mangle.mangle_response", notouch_mangle_rsp) |
||||
|
||||
@pytest.fixture(autouse=True) |
||||
def ignore_save(mocker): |
||||
mocker.patch("pappyproxy.http.Request.deep_save", func_ignored_deferred) |
||||
|
||||
#################### |
||||
## 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) |
||||
|
||||
def notouch_mangle_req(request, conn_id): |
||||
orig_req = http.Request(request.full_request) |
||||
orig_req.port = request.port |
||||
orig_req.is_ssl = request.is_ssl |
||||
d = mock_deferred(orig_req) |
||||
return d |
||||
|
||||
def notouch_mangle_rsp(response, conn_id): |
||||
req = http.Request() |
||||
orig_rsp = http.Response(response.full_response) |
||||
req.response = orig_rsp |
||||
d = mock_deferred(req) |
||||
return d |
||||
|
||||
def req_mangler_change(request, conn_id): |
||||
req = http.Request('GET /mangled HTTP/1.1\r\n\r\n') |
||||
d = mock_deferred(req) |
||||
return d |
||||
|
||||
def rsp_mangler_change(request, conn_id): |
||||
req = http.Request() |
||||
rsp = http.Response('HTTP/1.1 500 MANGLED\r\n\r\n') |
||||
req.response = rsp |
||||
d = mock_deferred(req) |
||||
return d |
||||
|
||||
#################### |
||||
## 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(): |
||||
with pytest.raises(NotImplementedError): |
||||
reactor.connectTCP("www.google.com", "80", ServerFactory) |
||||
with pytest.raises(NotImplementedError): |
||||
reactor.connectSSL("www.google.com", "80", ServerFactory) |
||||
|
||||
#################### |
||||
## Proxy Server Tests |
||||
|
||||
def test_proxy_server_connect(unconnected_proxyserver, mocker): |
||||
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 |
||||
|
||||
def test_proxy_server_basic(proxyserver, mocker): |
||||
mocker.patch("twisted.internet.reactor.connectSSL") |
||||
mocker.patch('pappyproxy.proxy.ProxyServer.setRawMode') |
||||
proxyserver.lineReceived('GET / HTTP/1.1') |
||||
proxyserver.lineReceived('') |
||||
|
||||
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_client_basic(mocker, proxy_connection): |
||||
mocker.patch('pappyproxy.mangle.mangle_request', new=notouch_mangle_req) |
||||
mocker.patch('pappyproxy.mangle.mangle_response', new=notouch_mangle_rsp) |
||||
# Make the connection |
||||
(prot, sent, resp_deferred) = yield proxy_connection('GET / HTTP/1.1\r\n\r\n') |
||||
assert sent == '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 resp_deferred |
||||
response = ret_req.response.full_response |
||||
assert response == 'HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n' |
||||
|
||||
@pytest.inlineCallbacks |
||||
def test_proxy_client_mangle_req(mocker, proxy_connection): |
||||
mocker.patch('pappyproxy.mangle.mangle_request', new=req_mangler_change) |
||||
mocker.patch('pappyproxy.mangle.mangle_response', new=notouch_mangle_rsp) |
||||
|
||||
# Make the connection |
||||
(prot, sent, resp_deferred) = yield proxy_connection('GET / HTTP/1.1\r\n\r\n') |
||||
assert sent == 'GET /mangled HTTP/1.1\r\n\r\n' |
||||
|
||||
@pytest.inlineCallbacks |
||||
def test_proxy_client_basic(mocker, proxy_connection): |
||||
mocker.patch('pappyproxy.mangle.mangle_request', new=notouch_mangle_req) |
||||
mocker.patch('pappyproxy.mangle.mangle_response', new=rsp_mangler_change) |
||||
# Make the connection |
||||
(prot, sent, resp_deferred) = yield proxy_connection('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 resp_deferred |
||||
response = ret_req.response.full_response |
||||
assert response == 'HTTP/1.1 500 MANGLED\r\n\r\n' |
@ -0,0 +1,42 @@ |
||||
import pytest |
||||
from twisted.internet import defer |
||||
|
||||
class ClassDeleted(): |
||||
pass |
||||
|
||||
def func_deleted(*args, **kwargs): |
||||
raise NotImplementedError() |
||||
|
||||
def func_ignored(*args, **kwargs): |
||||
pass |
||||
|
||||
def func_ignored_deferred(*args, **kwargs): |
||||
return mock_deferred(None) |
||||
|
||||
def mock_deferred(value): |
||||
# Generates a function that can be used to make a deferred that can be used |
||||
# to mock out deferred-returning responses |
||||
def g(data): |
||||
return value |
||||
d = defer.Deferred() |
||||
d.addCallback(g) |
||||
d.callback(None) |
||||
return d |
||||
|
||||
@pytest.fixture(autouse=True) |
||||
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) |
||||
|
||||
@pytest.fixture |
||||
def ignore_tcp(mocker): |
||||
# Don't make tcp connections |
||||
mocker.patch("twisted.internet.reactor.connectTCP", new=func_ignored) |
||||
mocker.patch("twisted.internet.reactor.connectSSL", new=func_ignored) |
||||
|
||||
@pytest.fixture(autouse=True) |
||||
def no_database(mocker): |
||||
# Don't make database queries |
||||
mocker.patch("twisted.enterprise.adbapi.ConnectionPool", |
||||
new=ClassDeleted) |
Loading…
Reference in new issue