aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/remote_debug.py19
-rw-r--r--mitmproxy/builtins/dumper.py79
-rw-r--r--mitmproxy/console/flowlist.py6
-rw-r--r--mitmproxy/console/flowview.py31
-rw-r--r--mitmproxy/contentviews.py17
-rw-r--r--mitmproxy/ctx.py2
-rw-r--r--mitmproxy/filt.py7
-rw-r--r--mitmproxy/protocol/http2.py2
-rw-r--r--mitmproxy/web/app.py3
-rw-r--r--netlib/http/url.py1
-rw-r--r--netlib/strutils.py3
-rw-r--r--test/mitmproxy/test_contentview.py4
-rw-r--r--test/mitmproxy/test_protocol_http2.py75
13 files changed, 148 insertions, 101 deletions
diff --git a/examples/remote_debug.py b/examples/remote_debug.py
new file mode 100644
index 00000000..fb864f78
--- /dev/null
+++ b/examples/remote_debug.py
@@ -0,0 +1,19 @@
+"""
+This script enables remote debugging of the mitmproxy *UI* with PyCharm.
+For general debugging purposes, it is easier to just debug mitmdump within PyCharm.
+
+Usage:
+ - pip install pydevd on the mitmproxy machine
+ - Open the Run/Debug Configuration dialog box in PyCharm, and select the Python Remote Debug configuration type.
+ - Debugging works in the way that mitmproxy connects to the debug server on startup.
+ Specify host and port that mitmproxy can use to reach your PyCharm instance on startup.
+ - Adjust this inline script accordingly.
+ - Start debug server in PyCharm
+ - Set breakpoints
+ - Start mitmproxy -s remote_debug.py
+"""
+
+
+def start():
+ import pydevd
+ pydevd.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True)
diff --git a/mitmproxy/builtins/dumper.py b/mitmproxy/builtins/dumper.py
index 34d3632e..74c2e6b2 100644
--- a/mitmproxy/builtins/dumper.py
+++ b/mitmproxy/builtins/dumper.py
@@ -5,6 +5,8 @@ import traceback
import click
+import typing # noqa
+
from mitmproxy import contentviews
from mitmproxy import ctx
from mitmproxy import exceptions
@@ -19,12 +21,25 @@ def indent(n, text):
return "\n".join(pad + i for i in l)
-class Dumper():
+class Dumper(object):
def __init__(self):
- self.filter = None
- self.flow_detail = None
- self.outfp = None
- self.showhost = None
+ self.filter = None # type: filt.TFilter
+ self.flow_detail = None # type: int
+ self.outfp = None # type: typing.io.TextIO
+ self.showhost = None # type: bool
+
+ def configure(self, options, updated):
+ if options.filtstr:
+ self.filter = filt.parse(options.filtstr)
+ if not self.filter:
+ raise exceptions.OptionsError(
+ "Invalid filter expression: %s" % options.filtstr
+ )
+ else:
+ self.filter = None
+ self.flow_detail = options.flow_detail
+ self.outfp = options.tfile
+ self.showhost = options.showhost
def echo(self, text, ident=None, **style):
if ident:
@@ -59,7 +74,7 @@ class Dumper():
self.echo("")
try:
- type, lines = contentviews.get_content_view(
+ _, lines = contentviews.get_content_view(
contentviews.get("Auto"),
content,
headers=getattr(message, "headers", None)
@@ -67,7 +82,7 @@ class Dumper():
except exceptions.ContentViewException:
s = "Content viewer failed: \n" + traceback.format_exc()
ctx.log.debug(s)
- type, lines = contentviews.get_content_view(
+ _, lines = contentviews.get_content_view(
contentviews.get("Raw"),
content,
headers=getattr(message, "headers", None)
@@ -114,9 +129,8 @@ class Dumper():
if flow.client_conn:
client = click.style(
strutils.escape_control_characters(
- flow.client_conn.address.host
- ),
- bold=True
+ repr(flow.client_conn.address)
+ )
)
elif flow.request.is_replay:
client = click.style("[replay]", fg="yellow", bold=True)
@@ -139,17 +153,23 @@ class Dumper():
url = flow.request.url
url = click.style(strutils.escape_control_characters(url), bold=True)
- httpversion = ""
+ http_version = ""
if flow.request.http_version not in ("HTTP/1.1", "HTTP/1.0"):
# We hide "normal" HTTP 1.
- httpversion = " " + flow.request.http_version
+ http_version = " " + flow.request.http_version
- line = "{stickycookie}{client} {method} {url}{httpversion}".format(
- stickycookie=stickycookie,
+ if self.flow_detail >= 2:
+ linebreak = "\n "
+ else:
+ linebreak = ""
+
+ line = "{client}: {linebreak}{stickycookie}{method} {url}{http_version}".format(
client=client,
+ stickycookie=stickycookie,
+ linebreak=linebreak,
method=method,
url=url,
- httpversion=httpversion
+ http_version=http_version
)
self.echo(line)
@@ -185,9 +205,14 @@ class Dumper():
size = human.pretty_size(len(flow.response.raw_content))
size = click.style(size, bold=True)
- arrows = click.style(" <<", bold=True)
+ arrows = click.style(" <<", bold=True)
+ if self.flow_detail == 1:
+ # This aligns the HTTP response code with the HTTP request method:
+ # 127.0.0.1:59519: GET http://example.com/
+ # << 304 Not Modified 0b
+ arrows = " " * (len(repr(flow.client_conn.address)) - 2) + arrows
- line = "{replay} {arrows} {code} {reason} {size}".format(
+ line = "{replay}{arrows} {code} {reason} {size}".format(
replay=replay,
arrows=arrows,
code=code,
@@ -211,25 +236,12 @@ class Dumper():
def match(self, f):
if self.flow_detail == 0:
return False
- if not self.filt:
+ if not self.filter:
return True
- elif f.match(self.filt):
+ elif f.match(self.filter):
return True
return False
- def configure(self, options, updated):
- if options.filtstr:
- self.filt = filt.parse(options.filtstr)
- if not self.filt:
- raise exceptions.OptionsError(
- "Invalid filter expression: %s" % options.filtstr
- )
- else:
- self.filt = None
- self.flow_detail = options.flow_detail
- self.outfp = options.tfile
- self.showhost = options.showhost
-
def response(self, f):
if self.match(f):
self.echo_flow(f)
@@ -239,8 +251,7 @@ class Dumper():
self.echo_flow(f)
def tcp_message(self, f):
- # FIXME: Filter should be applied here
- if self.options.flow_detail == 0:
+ if not self.match(f):
return
message = f.messages[-1]
direction = "->" if message.from_client else "<-"
diff --git a/mitmproxy/console/flowlist.py b/mitmproxy/console/flowlist.py
index 8aea9ea2..43742083 100644
--- a/mitmproxy/console/flowlist.py
+++ b/mitmproxy/console/flowlist.py
@@ -182,7 +182,8 @@ class ConnectionItem(urwid.WidgetWrap):
self.flow.accept_intercept(self.master)
signals.flowlist_change.send(self)
elif key == "d":
- self.flow.kill(self.master)
+ if not self.flow.reply.acked:
+ self.flow.kill(self.master)
self.state.delete_flow(self.flow)
signals.flowlist_change.send(self)
elif key == "D":
@@ -245,7 +246,8 @@ class ConnectionItem(urwid.WidgetWrap):
callback = self.save_flows_prompt,
)
elif key == "X":
- self.flow.kill(self.master)
+ if not self.flow.reply.acked:
+ self.flow.kill(self.master)
elif key == "enter":
if self.flow.request:
self.master.view_flow(self.flow)
diff --git a/mitmproxy/console/flowview.py b/mitmproxy/console/flowview.py
index 789066fc..22481f69 100644
--- a/mitmproxy/console/flowview.py
+++ b/mitmproxy/console/flowview.py
@@ -6,6 +6,7 @@ import sys
import traceback
import urwid
+from typing import Optional, Union # noqa
from mitmproxy import contentviews
from mitmproxy import controller
@@ -105,7 +106,8 @@ footer = [
class FlowViewHeader(urwid.WidgetWrap):
def __init__(self, master, f):
- self.master, self.flow = master, f
+ self.master = master # type: "mitmproxy.console.master.ConsoleMaster"
+ self.flow = f # type: models.HTTPFlow
self._w = common.format_flow(
f,
False,
@@ -530,13 +532,6 @@ class FlowView(tabs.Tabs):
)
signals.flow_change.send(self, flow = self.flow)
- def delete_body(self, t):
- if self.tab_offset == TAB_REQ:
- self.flow.request.content = None
- else:
- self.flow.response.content = None
- signals.flow_change.send(self, flow = self.flow)
-
def keypress(self, size, key):
key = super(self.__class__, self).keypress(size, key)
@@ -545,6 +540,8 @@ class FlowView(tabs.Tabs):
return
key = common.shortcuts(key)
+
+ conn = None # type: Optional[Union[models.HTTPRequest, models.HTTPResponse]]
if self.tab_offset == TAB_REQ:
conn = self.flow.request
elif self.tab_offset == TAB_RESP:
@@ -569,7 +566,8 @@ class FlowView(tabs.Tabs):
else:
self.view_next_flow(self.flow)
f = self.flow
- f.kill(self.master)
+ if not f.reply.acked:
+ f.kill(self.master)
self.state.delete_flow(f)
elif key == "D":
f = self.master.duplicate_flow(self.flow)
@@ -691,15 +689,8 @@ class FlowView(tabs.Tabs):
args = (scope, self.flow, common.copy_to_clipboard_or_prompt)
)
elif key == "x":
- signals.status_prompt_onekey.send(
- prompt = "Delete body",
- keys = (
- ("completely", "c"),
- ("mark as missing", "m"),
- ),
- callback = self.delete_body
- )
- key = None
+ conn.content = None
+ signals.flow_change.send(self, flow=self.flow)
elif key == "v":
if conn.raw_content:
t = conn.headers.get("content-type")
@@ -713,7 +704,9 @@ class FlowView(tabs.Tabs):
self.flow.backup()
e = conn.headers.get("content-encoding", "identity")
if e != "identity":
- if not conn.decode():
+ try:
+ conn.decode()
+ except ValueError:
signals.status_message.send(
message = "Could not decode - invalid data?"
)
diff --git a/mitmproxy/contentviews.py b/mitmproxy/contentviews.py
index afdaad7f..e155bc01 100644
--- a/mitmproxy/contentviews.py
+++ b/mitmproxy/contentviews.py
@@ -20,6 +20,8 @@ import logging
import subprocess
import sys
+from typing import Mapping # noqa
+
import html2text
import lxml.etree
import lxml.html
@@ -76,6 +78,7 @@ def pretty_json(s):
def format_dict(d):
+ # type: (Mapping[Union[str,bytes], Union[str,bytes]]) -> Generator[Tuple[Union[str,bytes], Union[str,bytes]]]
"""
Helper function that transforms the given dictionary into a list of
("key", key )
@@ -85,7 +88,7 @@ def format_dict(d):
max_key_len = max(len(k) for k in d.keys())
max_key_len = min(max_key_len, KEY_MAX)
for key, value in d.items():
- key += ":"
+ key += b":" if isinstance(key, bytes) else u":"
key = key.ljust(max_key_len + 2)
yield [
("header", key),
@@ -106,12 +109,16 @@ class View(object):
prompt = ()
content_types = []
- def __call__(self, data, **metadata):
+ def __call__(
+ self,
+ data, # type: bytes
+ **metadata
+ ):
"""
Transform raw data into human-readable output.
Args:
- data: the data to decode/format as bytes.
+ data: the data to decode/format.
metadata: optional keyword-only arguments for metadata. Implementations must not
rely on a given argument being present.
@@ -278,6 +285,10 @@ class ViewURLEncoded(View):
content_types = ["application/x-www-form-urlencoded"]
def __call__(self, data, **metadata):
+ try:
+ data = data.decode("ascii", "strict")
+ except ValueError:
+ return None
d = url.decode(data)
return "URLEncoded form", format_dict(multidict.MultiDict(d))
diff --git a/mitmproxy/ctx.py b/mitmproxy/ctx.py
index fcfdfd0b..5d2905fa 100644
--- a/mitmproxy/ctx.py
+++ b/mitmproxy/ctx.py
@@ -1,4 +1,4 @@
from typing import Callable # noqa
master = None # type: "mitmproxy.flow.FlowMaster"
-log = None # type: Callable[[str], None]
+log = None # type: "mitmproxy.controller.Log"
diff --git a/mitmproxy/filt.py b/mitmproxy/filt.py
index 6bdc1f03..67915e5b 100644
--- a/mitmproxy/filt.py
+++ b/mitmproxy/filt.py
@@ -39,9 +39,12 @@ import functools
from mitmproxy.models.http import HTTPFlow
from mitmproxy.models.tcp import TCPFlow
+from mitmproxy.models.flow import Flow
+
from netlib import strutils
import pyparsing as pp
+from typing import Callable
def only(*types):
@@ -480,7 +483,11 @@ def _make():
bnf = _make()
+TFilter = Callable[[Flow], bool]
+
+
def parse(s):
+ # type: (str) -> TFilter
try:
filt = bnf.parseString(s, parseAll=True)[0]
filt.pattern = s
diff --git a/mitmproxy/protocol/http2.py b/mitmproxy/protocol/http2.py
index 1285e10e..8308f44d 100644
--- a/mitmproxy/protocol/http2.py
+++ b/mitmproxy/protocol/http2.py
@@ -584,6 +584,8 @@ class Http2SingleStreamLayer(http._HttpTransmissionLayer, basethread.BaseThread)
except exceptions.ProtocolException as e: # pragma: no cover
self.log(repr(e), "info")
self.log(traceback.format_exc(), "debug")
+ except exceptions.Kill:
+ self.log("Connection killed", "info")
if not self.zombie:
self.zombie = time.time()
diff --git a/mitmproxy/web/app.py b/mitmproxy/web/app.py
index eaef887a..e55df1f6 100644
--- a/mitmproxy/web/app.py
+++ b/mitmproxy/web/app.py
@@ -230,7 +230,8 @@ class AcceptFlow(RequestHandler):
class FlowHandler(RequestHandler):
def delete(self, flow_id):
- self.flow.kill(self.master)
+ if not self.flow.reply.acked:
+ self.flow.kill(self.master)
self.state.delete_flow(self.flow)
def put(self, flow_id):
diff --git a/netlib/http/url.py b/netlib/http/url.py
index 2fc6e7ee..1c8c007a 100644
--- a/netlib/http/url.py
+++ b/netlib/http/url.py
@@ -82,6 +82,7 @@ def unparse(scheme, host, port, path=""):
def encode(s):
+ # type: (six.text_type, bytes) -> str
"""
Takes a list of (key, value) tuples and returns a urlencoded string.
"""
diff --git a/netlib/strutils.py b/netlib/strutils.py
index 32e77927..96c8b10f 100644
--- a/netlib/strutils.py
+++ b/netlib/strutils.py
@@ -51,8 +51,7 @@ else:
def escape_control_characters(text, keep_spacing=True):
"""
- Replace all unicode C1 control characters from the given text with their respective control pictures.
- For example, a null byte is replaced with the unicode character "\u2400".
+ Replace all unicode C1 control characters from the given text with a single "."
Args:
keep_spacing: If True, tabs and newlines will not be replaced.
diff --git a/test/mitmproxy/test_contentview.py b/test/mitmproxy/test_contentview.py
index 2db9ab40..aad53b37 100644
--- a/test/mitmproxy/test_contentview.py
+++ b/test/mitmproxy/test_contentview.py
@@ -59,10 +59,10 @@ class TestContentView:
assert f[0] == "Query"
def test_view_urlencoded(self):
- d = url.encode([("one", "two"), ("three", "four")])
+ d = url.encode([("one", "two"), ("three", "four")]).encode()
v = cv.ViewURLEncoded()
assert v(d)
- d = url.encode([("adsfa", "")])
+ d = url.encode([("adsfa", "")]).encode()
v = cv.ViewURLEncoded()
assert v(d)
diff --git a/test/mitmproxy/test_protocol_http2.py b/test/mitmproxy/test_protocol_http2.py
index afbffb67..aa096a72 100644
--- a/test/mitmproxy/test_protocol_http2.py
+++ b/test/mitmproxy/test_protocol_http2.py
@@ -30,7 +30,7 @@ logging.getLogger("PIL.PngImagePlugin").setLevel(logging.WARNING)
requires_alpn = pytest.mark.skipif(
not netlib.tcp.HAS_ALPN,
- reason="requires OpenSSL with ALPN support")
+ reason='requires OpenSSL with ALPN support')
class _Http2ServerBase(netlib_tservers.ServerTestBase):
@@ -80,7 +80,7 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase):
print(traceback.format_exc())
break
- def handle_server_event(self, h2_conn, rfile, wfile):
+ def handle_server_event(self, event, h2_conn, rfile, wfile):
raise NotImplementedError()
@@ -88,7 +88,6 @@ class _Http2TestBase(object):
@classmethod
def setup_class(cls):
- cls.masteroptions = options.Options()
opts = cls.get_options()
cls.config = ProxyConfig(opts)
@@ -145,12 +144,14 @@ class _Http2TestBase(object):
wfile,
h2_conn,
stream_id=1,
- headers=[],
+ headers=None,
body=b'',
end_stream=None,
priority_exclusive=None,
priority_depends_on=None,
priority_weight=None):
+ if headers is None:
+ headers = []
if end_stream is None:
end_stream = (len(body) == 0)
@@ -172,12 +173,12 @@ class _Http2TestBase(object):
class _Http2Test(_Http2TestBase, _Http2ServerBase):
@classmethod
- def setup_class(self):
+ def setup_class(cls):
_Http2TestBase.setup_class()
_Http2ServerBase.setup_class()
@classmethod
- def teardown_class(self):
+ def teardown_class(cls):
_Http2TestBase.teardown_class()
_Http2ServerBase.teardown_class()
@@ -187,7 +188,7 @@ class TestSimple(_Http2Test):
request_body_buffer = b''
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
elif isinstance(event, h2.events.RequestReceived):
@@ -214,7 +215,7 @@ class TestSimple(_Http2Test):
wfile.write(h2_conn.data_to_send())
wfile.flush()
elif isinstance(event, h2.events.DataReceived):
- self.request_body_buffer += event.data
+ cls.request_body_buffer += event.data
return True
def test_simple(self):
@@ -225,7 +226,7 @@ class TestSimple(_Http2Test):
client.wfile,
h2_conn,
headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -269,7 +270,7 @@ class TestSimple(_Http2Test):
class TestRequestWithPriority(_Http2Test):
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
elif isinstance(event, h2.events.RequestReceived):
@@ -301,14 +302,14 @@ class TestRequestWithPriority(_Http2Test):
client.wfile,
h2_conn,
headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
],
- priority_exclusive = True,
- priority_depends_on = 42424242,
- priority_weight = 42,
+ priority_exclusive=True,
+ priority_depends_on=42424242,
+ priority_weight=42,
)
done = False
@@ -343,7 +344,7 @@ class TestRequestWithPriority(_Http2Test):
client.wfile,
h2_conn,
headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -381,11 +382,11 @@ class TestPriority(_Http2Test):
priority_data = None
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
elif isinstance(event, h2.events.PriorityUpdated):
- self.priority_data = (event.exclusive, event.depends_on, event.weight)
+ cls.priority_data = (event.exclusive, event.depends_on, event.weight)
elif isinstance(event, h2.events.RequestReceived):
import warnings
with warnings.catch_warnings():
@@ -415,7 +416,7 @@ class TestPriority(_Http2Test):
client.wfile,
h2_conn,
headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -451,11 +452,11 @@ class TestPriorityWithExistingStream(_Http2Test):
priority_data = []
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
elif isinstance(event, h2.events.PriorityUpdated):
- self.priority_data.append((event.exclusive, event.depends_on, event.weight))
+ cls.priority_data.append((event.exclusive, event.depends_on, event.weight))
elif isinstance(event, h2.events.RequestReceived):
assert not event.priority_updated
@@ -486,7 +487,7 @@ class TestPriorityWithExistingStream(_Http2Test):
client.wfile,
h2_conn,
headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -527,7 +528,7 @@ class TestPriorityWithExistingStream(_Http2Test):
class TestStreamResetFromServer(_Http2Test):
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
elif isinstance(event, h2.events.RequestReceived):
@@ -543,7 +544,7 @@ class TestStreamResetFromServer(_Http2Test):
client.wfile,
h2_conn,
headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -578,7 +579,7 @@ class TestStreamResetFromServer(_Http2Test):
class TestBodySizeLimit(_Http2Test):
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
return True
@@ -592,7 +593,7 @@ class TestBodySizeLimit(_Http2Test):
client.wfile,
h2_conn,
headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -627,7 +628,7 @@ class TestBodySizeLimit(_Http2Test):
class TestPushPromise(_Http2Test):
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
elif isinstance(event, h2.events.RequestReceived):
@@ -637,14 +638,14 @@ class TestPushPromise(_Http2Test):
h2_conn.send_headers(1, [(':status', '200')])
h2_conn.push_stream(1, 2, [
- (':authority', "127.0.0.1:%s" % self.port),
+ (':authority', "127.0.0.1:{}".format(cls.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/pushed_stream_foo'),
('foo', 'bar')
])
h2_conn.push_stream(1, 4, [
- (':authority', "127.0.0.1:%s" % self.port),
+ (':authority', "127.0.0.1:{}".format(cls.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/pushed_stream_bar'),
@@ -675,7 +676,7 @@ class TestPushPromise(_Http2Test):
client, h2_conn = self._setup_connection()
self._send_request(client.wfile, h2_conn, stream_id=1, headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -728,7 +729,7 @@ class TestPushPromise(_Http2Test):
client, h2_conn = self._setup_connection()
self._send_request(client.wfile, h2_conn, stream_id=1, headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -780,7 +781,7 @@ class TestPushPromise(_Http2Test):
class TestConnectionLost(_Http2Test):
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.RequestReceived):
h2_conn.send_headers(1, [(':status', '200')])
wfile.write(h2_conn.data_to_send())
@@ -791,7 +792,7 @@ class TestConnectionLost(_Http2Test):
client, h2_conn = self._setup_connection()
self._send_request(client.wfile, h2_conn, stream_id=1, headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -822,12 +823,12 @@ class TestConnectionLost(_Http2Test):
class TestMaxConcurrentStreams(_Http2Test):
@classmethod
- def setup_class(self):
+ def setup_class(cls):
_Http2TestBase.setup_class()
_Http2ServerBase.setup_class(h2_server_settings={h2.settings.MAX_CONCURRENT_STREAMS: 2})
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.ConnectionTerminated):
return False
elif isinstance(event, h2.events.RequestReceived):
@@ -848,7 +849,7 @@ class TestMaxConcurrentStreams(_Http2Test):
# this will exceed MAX_CONCURRENT_STREAMS on the server connection
# and cause mitmproxy to throttle stream creation to the server
self._send_request(client.wfile, h2_conn, stream_id=id, headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),
@@ -883,7 +884,7 @@ class TestMaxConcurrentStreams(_Http2Test):
class TestConnectionTerminated(_Http2Test):
@classmethod
- def handle_server_event(self, event, h2_conn, rfile, wfile):
+ def handle_server_event(cls, event, h2_conn, rfile, wfile):
if isinstance(event, h2.events.RequestReceived):
h2_conn.close_connection(error_code=5, last_stream_id=42, additional_data=b'foobar')
wfile.write(h2_conn.data_to_send())
@@ -894,7 +895,7 @@ class TestConnectionTerminated(_Http2Test):
client, h2_conn = self._setup_connection()
self._send_request(client.wfile, h2_conn, headers=[
- (':authority', "127.0.0.1:%s" % self.server.server.address.port),
+ (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
(':method', 'GET'),
(':scheme', 'https'),
(':path', '/'),