aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/addons/proxyauth.py45
-rw-r--r--mitmproxy/addons/view.py3
-rw-r--r--mitmproxy/io/compat.py19
-rw-r--r--mitmproxy/net/http/url.py24
-rw-r--r--mitmproxy/options.py7
-rw-r--r--setup.cfg5
-rw-r--r--setup.py4
-rw-r--r--test/mitmproxy/addons/test_proxyauth.py55
-rw-r--r--test/mitmproxy/data/dumpfile-01840
-rw-r--r--test/mitmproxy/net/http/test_cookies.py3
-rw-r--r--test/mitmproxy/net/http/test_message.py6
11 files changed, 127 insertions, 84 deletions
diff --git a/mitmproxy/addons/proxyauth.py b/mitmproxy/addons/proxyauth.py
index e656421e..5f884b55 100644
--- a/mitmproxy/addons/proxyauth.py
+++ b/mitmproxy/addons/proxyauth.py
@@ -47,12 +47,13 @@ class ProxyAuth:
self.nonanonymous = False
self.htpasswd = None
self.singleuser = None
+ self.ldapconn = None
self.ldapserver = None
self.authenticated = weakref.WeakKeyDictionary() # type: MutableMapping[connections.ClientConnection, Tuple[str, str]]
"""Contains all connections that are permanently authenticated after an HTTP CONNECT"""
def enabled(self) -> bool:
- return any([self.nonanonymous, self.htpasswd, self.singleuser, self.ldapserver])
+ return any([self.nonanonymous, self.htpasswd, self.singleuser, self.ldapconn, self.ldapserver])
def is_proxy_auth(self) -> bool:
"""
@@ -101,19 +102,17 @@ class ProxyAuth:
elif self.htpasswd:
if self.htpasswd.check_password(username, password):
return username, password
- elif self.ldapserver:
+ elif self.ldapconn:
if not username or not password:
return None
- dn = ctx.options.proxyauth.split(":")[2]
- parts = dn.split("?")
- conn = ldap3.Connection(
- self.ldapserver,
- parts[0] + username + parts[1],
- password,
- auto_bind=True)
- if conn:
- conn.search(parts[1][1:], '(' + parts[0] + username + ')', attributes=['objectclass'])
- if ctx.options.proxyauth.split(":")[3] in conn.entries[0]['objectclass']:
+ self.ldapconn.search(ctx.options.proxyauth.split(':')[4], '(cn=' + username + ')')
+ if self.ldapconn.response:
+ conn = ldap3.Connection(
+ self.ldapserver,
+ self.ldapconn.response[0]['dn'],
+ password,
+ auto_bind=True)
+ if conn:
return username, password
return None
@@ -146,19 +145,29 @@ class ProxyAuth:
"Could not open htpasswd file: %s" % p
)
elif ctx.options.proxyauth.startswith("ldap"):
- parts = ctx.options.proxyauth.split(":")
- if len(parts) != 4:
+ parts = ctx.options.proxyauth.split(':')
+ security = parts[0]
+ ldap_server = parts[1]
+ dn_baseauth = parts[2]
+ password_baseauth = parts[3]
+ if len(parts) != 5:
raise exceptions.OptionsError(
"Invalid ldap specification"
)
- if parts[0] == "ldaps":
- server = ldap3.Server(parts[1], use_ssl=True)
- elif parts[0] == "ldap":
- server = ldap3.Server(parts[1])
+ if security == "ldaps":
+ server = ldap3.Server(ldap_server, use_ssl=True)
+ elif security == "ldap":
+ server = ldap3.Server(ldap_server)
else:
raise exceptions.OptionsError(
"Invalid ldap specfication on the first part"
)
+ conn = ldap3.Connection(
+ server,
+ dn_baseauth,
+ password_baseauth,
+ auto_bind=True)
+ self.ldapconn = conn
self.ldapserver = server
else:
parts = ctx.options.proxyauth.split(':')
diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py
index dd579585..13a17c56 100644
--- a/mitmproxy/addons/view.py
+++ b/mitmproxy/addons/view.py
@@ -10,7 +10,6 @@ The View:
"""
import collections
import typing
-import datetime
import blinker
import sortedcontainers
@@ -69,7 +68,7 @@ class _OrderKey:
class OrderRequestStart(_OrderKey):
- def generate(self, f: http.HTTPFlow) -> datetime.datetime:
+ def generate(self, f: http.HTTPFlow) -> int:
return f.request.timestamp_start or 0
diff --git a/mitmproxy/io/compat.py b/mitmproxy/io/compat.py
index 9d95f602..99da496d 100644
--- a/mitmproxy/io/compat.py
+++ b/mitmproxy/io/compat.py
@@ -75,6 +75,8 @@ def convert_018_019(data):
data["client_conn"]["cipher_name"] = None
data["client_conn"]["tls_version"] = None
data["server_conn"]["alpn_proto_negotiated"] = None
+ if data["server_conn"]["via"]:
+ data["server_conn"]["via"]["alpn_proto_negotiated"] = None
data["mode"] = "regular"
data["metadata"] = dict()
data["version"] = (0, 19)
@@ -96,6 +98,13 @@ def convert_100_200(data):
data["server_conn"]["source_address"] = data["server_conn"]["source_address"]["address"]
if data["server_conn"]["ip_address"]:
data["server_conn"]["ip_address"] = data["server_conn"]["ip_address"]["address"]
+
+ if data["server_conn"]["via"]:
+ data["server_conn"]["via"]["address"] = data["server_conn"]["via"]["address"]["address"]
+ data["server_conn"]["via"]["source_address"] = data["server_conn"]["via"]["source_address"]["address"]
+ if data["server_conn"]["via"]["ip_address"]:
+ data["server_conn"]["via"]["ip_address"] = data["server_conn"]["via"]["ip_address"]["address"]
+
return data
@@ -129,6 +138,14 @@ def convert_4_5(data):
)
data["client_conn"]["id"] = client_connections.setdefault(client_conn_key, str(uuid.uuid4()))
data["server_conn"]["id"] = server_connections.setdefault(server_conn_key, str(uuid.uuid4()))
+
+ if data["server_conn"]["via"]:
+ server_conn_key = (
+ data["server_conn"]["via"]["timestamp_start"],
+ *data["server_conn"]["via"]["source_address"]
+ )
+ data["server_conn"]["via"]["id"] = server_connections.setdefault(server_conn_key, str(uuid.uuid4()))
+
return data
@@ -142,7 +159,7 @@ def _convert_dict_keys(o: Any) -> Any:
def _convert_dict_vals(o: dict, values_to_convert: dict) -> dict:
for k, v in values_to_convert.items():
if not o or k not in o:
- continue
+ continue # pragma: no cover
if v is True:
o[k] = strutils.always_str(o[k])
else:
diff --git a/mitmproxy/net/http/url.py b/mitmproxy/net/http/url.py
index f2c8c473..86f65cfd 100644
--- a/mitmproxy/net/http/url.py
+++ b/mitmproxy/net/http/url.py
@@ -5,22 +5,6 @@ from typing import Tuple
from mitmproxy.net import check
-# PY2 workaround
-def decode_parse_result(result, enc):
- if hasattr(result, "decode"):
- return result.decode(enc)
- else:
- return urllib.parse.ParseResult(*[x.decode(enc) for x in result])
-
-
-# PY2 workaround
-def encode_parse_result(result, enc):
- if hasattr(result, "encode"):
- return result.encode(enc)
- else:
- return urllib.parse.ParseResult(*[x.encode(enc) for x in result])
-
-
def parse(url):
"""
URL-parsing function that checks that
@@ -47,12 +31,12 @@ def parse(url):
# this should not raise a ValueError,
# but we try to be very forgiving here and accept just everything.
- # decode_parse_result(parsed, "ascii")
else:
host = parsed.hostname.encode("idna")
- parsed = encode_parse_result(parsed, "ascii")
+ if isinstance(parsed, urllib.parse.ParseResult):
+ parsed = parsed.encode("ascii")
- port = parsed.port
+ port = parsed.port # Returns None if port number invalid in Py3.5. Will throw ValueError in Py3.6
if not port:
port = 443 if parsed.scheme == b"https" else 80
@@ -64,8 +48,6 @@ def parse(url):
if not check.is_valid_host(host):
raise ValueError("Invalid Host")
- if not check.is_valid_port(port):
- raise ValueError("Invalid Port")
return parsed.scheme, host, port, full_path
diff --git a/mitmproxy/options.py b/mitmproxy/options.py
index 1091fe78..058ea9e4 100644
--- a/mitmproxy/options.py
+++ b/mitmproxy/options.py
@@ -206,10 +206,11 @@ class Options(optmanager.OptManager):
authenticaiton but accept any credentials, start with "@" to specify
a path to an Apache htpasswd file, be of the form
"username:password", or be of the form
- "ldap[s]:url_server_ldap:dn:group", the dn must include "?", which will be
- the username prompted, and the group is the group the user must belong to
+ "ldap[s]:url_server_ldap:dn_auth:password:dn_subtree",
+ the dn_auth & password is the dn/pass used to authenticate
+ the dn subtree is the subtree that we will search to find the username
an example would be
- "ldap:ldap.forumsys.com:uid=?,dc=example,dc=com:person".
+ "ldap:localhost:cn=default,dc=example,dc=com:password:ou=application,dc=example,dc=com".
"""
)
self.add_option(
diff --git a/setup.cfg b/setup.cfg
index 993cad31..ebe76efd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -25,10 +25,7 @@ exclude =
mitmproxy/contentviews/wbxml.py
mitmproxy/contentviews/xml_html.py
mitmproxy/net/tcp.py
- mitmproxy/net/http/cookies.py
mitmproxy/net/http/encoding.py
- mitmproxy/net/http/message.py
- mitmproxy/net/http/url.py
mitmproxy/proxy/protocol/
mitmproxy/proxy/config.py
mitmproxy/proxy/root_context.py
@@ -36,7 +33,6 @@ exclude =
mitmproxy/tools/
mitmproxy/controller.py
mitmproxy/flow.py
- mitmproxy/io/compat.py
mitmproxy/master.py
pathod/pathoc.py
pathod/pathod.py
@@ -55,7 +51,6 @@ exclude =
mitmproxy/exceptions.py
mitmproxy/flow.py
mitmproxy/io/io.py
- mitmproxy/io/compat.py
mitmproxy/io/tnetstring.py
mitmproxy/log.py
mitmproxy/master.py
diff --git a/setup.py b/setup.py
index b75466fb..1466970b 100644
--- a/setup.py
+++ b/setup.py
@@ -71,7 +71,7 @@ setup(
"hyperframe>=5.0, <6",
"jsbeautifier>=1.6.3, <1.7",
"kaitaistruct>=0.7, <0.8",
- "ldap3>=2.2.0, <=2.2.3",
+ "ldap3>=2.2.0, <2.3",
"passlib>=1.6.5, <1.8",
"pyasn1>=0.1.9, <0.3",
"pyOpenSSL>=16.0, <17.1",
@@ -112,7 +112,7 @@ setup(
'contentviews': [
],
'examples': [
- "beautifulsoup4>=4.4.1, <4.6",
+ "beautifulsoup4>=4.4.1, <4.7",
"Pillow>=3.2, <4.2",
]
}
diff --git a/test/mitmproxy/addons/test_proxyauth.py b/test/mitmproxy/addons/test_proxyauth.py
index 58e059ad..40044bf0 100644
--- a/test/mitmproxy/addons/test_proxyauth.py
+++ b/test/mitmproxy/addons/test_proxyauth.py
@@ -1,8 +1,8 @@
import binascii
-import ldap3
import pytest
+from unittest import mock
from mitmproxy import exceptions
from mitmproxy.addons import proxyauth
from mitmproxy.test import taddons
@@ -42,19 +42,21 @@ def test_configure():
ctx.configure(up, proxyauth=None)
assert not up.nonanonymous
- ctx.configure(up, proxyauth="ldap:fake_server:fake_dn:fake_group")
- assert up.ldapserver
-
- ctx.configure(up, proxyauth="ldap:fake_server:uid=?,dc=example,dc=com:person")
- assert up.ldapserver
- ctx.configure(up, proxyauth="ldaps:fake_server.com:uid=?,dc=example,dc=com:person")
- assert up.ldapserver
+ with mock.patch('ldap3.Server', return_value="ldap://fake_server:389 - cleartext"):
+ with mock.patch('ldap3.Connection', return_value="test"):
+ ctx.configure(up, proxyauth="ldap:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com")
+ assert up.ldapserver
+ ctx.configure(up, proxyauth="ldaps:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com")
+ assert up.ldapserver
with pytest.raises(exceptions.OptionsError):
+ ctx.configure(up, proxyauth="ldap:test:test:test")
+
+ with pytest.raises(IndexError):
ctx.configure(up, proxyauth="ldap:fake_serveruid=?dc=example,dc=com:person")
with pytest.raises(exceptions.OptionsError):
- ctx.configure(up, proxyauth="ldapssssssss:fake_server.com:uid=?,dc=example,dc=com:person")
+ ctx.configure(up, proxyauth="ldapssssssss:fake_server:dn:password:tree")
with pytest.raises(exceptions.OptionsError):
ctx.configure(
@@ -124,26 +126,21 @@ def test_check(monkeypatch):
)
assert not up.check(f)
- ctx.configure(
- up,
- proxyauth="ldap:fake-server:cn=?,ou=test,o=lab:test"
- )
- conn = ldap3.Connection("fake-server", user="cn=user0,ou=test,o=lab", password="password", client_strategy=ldap3.MOCK_SYNC)
- conn.bind()
- conn.strategy.add_entry('cn=user0,ou=test,o=lab', {'userPassword': 'test0', 'sn': 'user0_sn', 'revision': 0, 'objectClass': 'test'})
-
- def conn_mp(ldap, user, password, **kwargs):
- return conn
-
- monkeypatch.setattr(ldap3, "Connection", conn_mp)
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "user0", "test0"
- )
- assert up.check(f)
- f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
- "", ""
- )
- assert not up.check(f)
+ with mock.patch('ldap3.Server', return_value="ldap://fake_server:389 - cleartext"):
+ with mock.patch('ldap3.Connection', search="test"):
+ with mock.patch('ldap3.Connection.search', return_value="test"):
+ ctx.configure(
+ up,
+ proxyauth="ldap:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com"
+ )
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "test", "test"
+ )
+ assert up.check(f)
+ f.request.headers["Proxy-Authorization"] = proxyauth.mkauth(
+ "", ""
+ )
+ assert not up.check(f)
def test_authenticate():
diff --git a/test/mitmproxy/data/dumpfile-018 b/test/mitmproxy/data/dumpfile-018
index abe8b0b1..6a27b5a6 100644
--- a/test/mitmproxy/data/dumpfile-018
+++ b/test/mitmproxy/data/dumpfile-018
@@ -1,4 +1,4 @@
-5243:5:error;0:~11:intercepted;5:false!6:marked;5:false!2:id;36:55367415-10f5-4938-b69f-8a523394f947;7:request;396:10:stickyauth;5:false!7:content;0:,15:timestamp_start;18:1482157523.9086578^9:is_replay;5:false!4:path;1:/,4:host;15:www.example.com,17:first_line_format;8:relative;12:stickycookie;5:false!12:http_version;8:HTTP/1.1,6:method;3:GET,4:port;3:443#13:timestamp_end;18:1482157523.9086578^6:scheme;5:https,7:headers;82:29:10:User-Agent,11:curl/7.35.0,]26:4:Host,15:www.example.com,]15:6:Accept,3:*/*,]]}8:response;1851:6:reason;2:OK,12:http_version;8:HTTP/1.1,13:timestamp_end;17:1482157524.361187^11:status_code;3:200#7:content;1270:<!doctype html>
+7816:4:type;4:http;2:id;36:55367415-10f5-4938-b69f-8a523394f947;8:response;1851:15:timestamp_start;17:1482157524.361187^12:http_version;8:HTTP/1.1,7:content;1270:<!doctype html>
<html>
<head>
<title>Example Domain</title>
@@ -48,7 +48,7 @@
</div>
</body>
</html>
-,7:headers;410:25:13:Accept-Ranges,5:bytes,]35:13:Cache-Control,14:max-age=604800,]28:12:Content-Type,9:text/html,]40:4:Date,29:Mon, 19 Dec 2016 14:25:24 GMT,]22:4:Etag,11:"359670651",]43:7:Expires,29:Mon, 26 Dec 2016 14:25:24 GMT,]50:13:Last-Modified,29:Fri, 09 Aug 2013 23:54:35 GMT,]27:6:Server,14:ECS (iad/18CB),]26:4:Vary,15:Accept-Encoding,]16:7:X-Cache,3:HIT,]25:17:x-ec-custom-error,1:1,]25:14:Content-Length,4:1270,]]15:timestamp_start;17:1482157524.361187^}4:type;4:http;11:server_conn;2570:15:ssl_established;4:true!7:address;58:7:address;25:15:www.example.com;3:443#]8:use_ipv6;5:false!}10:ip_address;56:7:address;23:13:93.184.216.34;3:443#]8:use_ipv6;5:false!}3:via;0:~14:source_address;57:7:address;24:12:10.67.53.133;5:52775#]8:use_ipv6;5:false!}13:timestamp_end;0:~4:cert;2122:-----BEGIN CERTIFICATE-----
+,13:timestamp_end;17:1482157524.361187^11:status_code;3:200#6:reason;2:OK,7:headers;410:25:13:Accept-Ranges,5:bytes,]35:13:Cache-Control,14:max-age=604800,]28:12:Content-Type,9:text/html,]40:4:Date,29:Mon, 19 Dec 2016 14:25:24 GMT,]22:4:Etag,11:"359670651",]43:7:Expires,29:Mon, 26 Dec 2016 14:25:24 GMT,]50:13:Last-Modified,29:Fri, 09 Aug 2013 23:54:35 GMT,]27:6:Server,14:ECS (iad/18CB),]26:4:Vary,15:Accept-Encoding,]16:7:X-Cache,3:HIT,]25:17:x-ec-custom-error,1:1,]25:14:Content-Length,4:1270,]]}7:request;396:9:is_replay;5:false!17:first_line_format;8:relative;4:port;3:443#7:content;0:,12:stickycookie;5:false!6:method;3:GET,7:headers;82:29:10:User-Agent,11:curl/7.35.0,]26:4:Host,15:www.example.com,]15:6:Accept,3:*/*,]]15:timestamp_start;18:1482157523.9086578^12:http_version;8:HTTP/1.1,13:timestamp_end;18:1482157523.9086578^4:path;1:/,10:stickyauth;5:false!4:host;15:www.example.com,6:scheme;5:https,}7:version;13:1:0#2:18#1:2#]5:error;0:~11:intercepted;5:false!11:server_conn;5143:10:ip_address;56:8:use_ipv6;5:false!7:address;23:13:93.184.216.34;3:443#]}15:timestamp_start;18:1482157523.9086578^19:timestamp_tcp_setup;18:1482157524.0081189^15:ssl_established;4:true!14:source_address;57:8:use_ipv6;5:false!7:address;24:12:10.67.53.133;5:52775#]}19:timestamp_ssl_setup;17:1482157524.260993^4:cert;2122:-----BEGIN CERTIFICATE-----
MIIF8jCCBNqgAwIBAgIQDmTF+8I2reFLFyrrQceMsDANBgkqhkiG9w0BAQsFADBw
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
@@ -82,4 +82,38 @@ ieqRbcuFjmqfyPmUv1U9QoI4TQikpw7TZU0zYZANP4C/gj4Ry48/znmUaRvy2kvI
l7gRQ21qJTK5suoiYoYNo3J9T+pXPGU7Lydz/HwW+w0DpArtAaukI8aNX4ohFUKS
wDSiIIWIWJiJGbEeIO0TIFwEVWTOnbNl/faPXpk5IRXicapqiII=
-----END CERTIFICATE-----
-,15:timestamp_start;18:1482157523.9086578^3:sni;15:www.example.com;19:timestamp_ssl_setup;17:1482157524.260993^19:timestamp_tcp_setup;18:1482157524.0081189^}11:client_conn;216:15:ssl_established;4:true!7:address;53:7:address;20:9:127.0.0.1;5:52774#]8:use_ipv6;5:false!}10:clientcert;0:~13:timestamp_end;0:~15:timestamp_start;18:1482157522.8949482^19:timestamp_ssl_setup;18:1482157523.9086578^}7:version;13:1:0#2:18#1:2#]} \ No newline at end of file
+,13:timestamp_end;0:~3:via;2570:15:ssl_established;4:true!19:timestamp_tcp_setup;18:1482157524.0081189^19:timestamp_ssl_setup;17:1482157524.260993^3:via;0:~3:sni;15:www.example.com;10:ip_address;56:8:use_ipv6;5:false!7:address;23:13:93.184.216.34;3:443#]}15:timestamp_start;18:1482157523.9086578^14:source_address;57:8:use_ipv6;5:false!7:address;24:12:10.67.53.133;5:52775#]}4:cert;2122:-----BEGIN CERTIFICATE-----
+MIIF8jCCBNqgAwIBAgIQDmTF+8I2reFLFyrrQceMsDANBgkqhkiG9w0BAQsFADBw
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
+dXJhbmNlIFNlcnZlciBDQTAeFw0xNTExMDMwMDAwMDBaFw0xODExMjgxMjAwMDBa
+MIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxML
+TG9zIEFuZ2VsZXMxPDA6BgNVBAoTM0ludGVybmV0IENvcnBvcmF0aW9uIGZvciBB
+c3NpZ25lZCBOYW1lcyBhbmQgTnVtYmVyczETMBEGA1UECxMKVGVjaG5vbG9neTEY
+MBYGA1UEAxMPd3d3LmV4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAs0CWL2FjPiXBl61lRfvvE0KzLJmG9LWAC3bcBjgsH6NiVVo2dt6u
+Xfzi5bTm7F3K7srfUBYkLO78mraM9qizrHoIeyofrV/n+pZZJauQsPjCPxMEJnRo
+D8Z4KpWKX0LyDu1SputoI4nlQ/htEhtiQnuoBfNZxF7WxcxGwEsZuS1KcXIkHl5V
+RJOreKFHTaXcB1qcZ/QRaBIv0yhxvK1yBTwWddT4cli6GfHcCe3xGMaSL328Fgs3
+jYrvG29PueB6VJi/tbbPu6qTfwp/H1brqdjh29U52Bhb0fJkM9DWxCP/Cattcc7a
+z8EXnCO+LK8vkhw/kAiJWPKx4RBvgy73nwIDAQABo4ICUDCCAkwwHwYDVR0jBBgw
+FoAUUWj/kK8CB3U8zNllZGKiErhZcjswHQYDVR0OBBYEFKZPYB4fLdHn8SOgKpUW
+5Oia6m5IMIGBBgNVHREEejB4gg93d3cuZXhhbXBsZS5vcmeCC2V4YW1wbGUuY29t
+ggtleGFtcGxlLmVkdYILZXhhbXBsZS5uZXSCC2V4YW1wbGUub3Jngg93d3cuZXhh
+bXBsZS5jb22CD3d3dy5leGFtcGxlLmVkdYIPd3d3LmV4YW1wbGUubmV0MA4GA1Ud
+DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdQYDVR0f
+BG4wbDA0oDKgMIYuaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItaGEtc2Vy
+dmVyLWc0LmNybDA0oDKgMIYuaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTIt
+aGEtc2VydmVyLWc0LmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwBATAqMCgGCCsG
+AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAECAjCB
+gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
+dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E
+aWdpQ2VydFNIQTJIaWdoQXNzdXJhbmNlU2VydmVyQ0EuY3J0MAwGA1UdEwEB/wQC
+MAAwDQYJKoZIhvcNAQELBQADggEBAISomhGn2L0LJn5SJHuyVZ3qMIlRCIdvqe0Q
+6ls+C8ctRwRO3UU3x8q8OH+2ahxlQmpzdC5al4XQzJLiLjiJ2Q1p+hub8MFiMmVP
+PZjb2tZm2ipWVuMRM+zgpRVM6nVJ9F3vFfUSHOb4/JsEIUvPY+d8/Krc+kPQwLvy
+ieqRbcuFjmqfyPmUv1U9QoI4TQikpw7TZU0zYZANP4C/gj4Ry48/znmUaRvy2kvI
+l7gRQ21qJTK5suoiYoYNo3J9T+pXPGU7Lydz/HwW+w0DpArtAaukI8aNX4ohFUKS
+wDSiIIWIWJiJGbEeIO0TIFwEVWTOnbNl/faPXpk5IRXicapqiII=
+-----END CERTIFICATE-----
+,13:timestamp_end;0:~7:address;58:8:use_ipv6;5:false!7:address;25:15:www.example.com;3:443#]}}7:address;58:8:use_ipv6;5:false!7:address;25:15:www.example.com;3:443#]}3:sni;15:www.example.com;}11:client_conn;216:15:timestamp_start;18:1482157522.8949482^15:ssl_established;4:true!13:timestamp_end;0:~10:clientcert;0:~7:address;53:8:use_ipv6;5:false!7:address;20:9:127.0.0.1;5:52774#]}19:timestamp_ssl_setup;18:1482157523.9086578^}6:marked;5:false!} \ No newline at end of file
diff --git a/test/mitmproxy/net/http/test_cookies.py b/test/mitmproxy/net/http/test_cookies.py
index 680a5033..77549d9e 100644
--- a/test/mitmproxy/net/http/test_cookies.py
+++ b/test/mitmproxy/net/http/test_cookies.py
@@ -269,6 +269,9 @@ def test_refresh_cookie():
c = "MOO=BAR; Expires=Tue, 08-Mar-2011 00:20:38 GMT; Path=foo.com; Secure"
assert "00:21:38" in cookies.refresh_set_cookie_header(c, 60)
+ c = "rfoo=bar; Domain=reddit.com; expires=Thu, 31 Dec 2037; Path=/"
+ assert "expires" not in cookies.refresh_set_cookie_header(c, 60)
+
c = "foo,bar"
with pytest.raises(ValueError):
cookies.refresh_set_cookie_header(c, 60)
diff --git a/test/mitmproxy/net/http/test_message.py b/test/mitmproxy/net/http/test_message.py
index c6036697..512f3199 100644
--- a/test/mitmproxy/net/http/test_message.py
+++ b/test/mitmproxy/net/http/test_message.py
@@ -48,6 +48,12 @@ class TestMessageData:
assert data != 0
+ def test_serializable(self):
+ data1 = tutils.tresp(timestamp_start=42, timestamp_end=42).data
+ data2 = tutils.tresp().data.from_state(data1.get_state()) # ResponseData.from_state()
+
+ assert data1 == data2
+
class TestMessage: