eventsequence: coverage++

This commit is contained in:
Thomas Kriechbaumer 2017-02-09 11:56:38 +01:00
parent 5a3976c43e
commit 0299bb5b2e
8 changed files with 64 additions and 88 deletions

View File

@ -8,7 +8,7 @@ import types
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import ctx from mitmproxy import ctx
from mitmproxy import events from mitmproxy import eventsequence
import watchdog.events import watchdog.events
@ -141,7 +141,7 @@ class Script:
self.last_options = None self.last_options = None
self.should_reload = threading.Event() self.should_reload = threading.Event()
for i in events.Events: for i in eventsequence.Events:
if not hasattr(self, i): if not hasattr(self, i):
def mkprox(): def mkprox():
evt = i evt = i
@ -211,7 +211,7 @@ class ScriptLoader:
raise ValueError(str(e)) raise ValueError(str(e))
sc.load_script() sc.load_script()
for f in flows: for f in flows:
for evt, o in events.event_sequence(f): for evt, o in eventsequence.iterate(f):
sc.run(evt, o) sc.run(evt, o)
sc.done() sc.done()
return sc return sc

View File

@ -37,7 +37,7 @@ Events = frozenset([
]) ])
def event_sequence(f): def iterate(f):
if isinstance(f, http.HTTPFlow): if isinstance(f, http.HTTPFlow):
if f.request: if f.request:
yield "requestheaders", f yield "requestheaders", f
@ -70,4 +70,4 @@ def event_sequence(f):
yield "tcp_error", f yield "tcp_error", f
yield "tcp_end", f yield "tcp_end", f
else: else:
raise NotImplementedError raise ValueError

View File

@ -7,7 +7,7 @@ import sys
from mitmproxy import addonmanager from mitmproxy import addonmanager
from mitmproxy import options from mitmproxy import options
from mitmproxy import controller from mitmproxy import controller
from mitmproxy import events from mitmproxy import eventsequence
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import connections from mitmproxy import connections
from mitmproxy import http from mitmproxy import http
@ -91,7 +91,7 @@ class Master:
changed = False changed = False
try: try:
mtype, obj = self.event_queue.get(timeout=timeout) mtype, obj = self.event_queue.get(timeout=timeout)
if mtype not in events.Events: if mtype not in eventsequence.Events:
raise exceptions.ControlException( raise exceptions.ControlException(
"Unknown event %s" % repr(mtype) "Unknown event %s" % repr(mtype)
) )
@ -153,7 +153,7 @@ class Master:
f.request.port = self.server.config.upstream_server.address.port f.request.port = self.server.config.upstream_server.address.port
f.request.scheme = self.server.config.upstream_server.scheme f.request.scheme = self.server.config.upstream_server.scheme
f.reply = controller.DummyReply() f.reply = controller.DummyReply()
for e, o in events.event_sequence(f): for e, o in eventsequence.iterate(f):
getattr(self, e)(o) getattr(self, e)(o)
def load_flows(self, fr: io.FlowReader) -> int: def load_flows(self, fr: io.FlowReader) -> int:

View File

@ -3,7 +3,7 @@ This module provides a @concurrent decorator primitive to
offload computations from mitmproxy's main master thread. offload computations from mitmproxy's main master thread.
""" """
from mitmproxy import events from mitmproxy import eventsequence
from mitmproxy.types import basethread from mitmproxy.types import basethread
@ -12,7 +12,7 @@ class ScriptThread(basethread.BaseThread):
def concurrent(fn): def concurrent(fn):
if fn.__name__ not in events.Events - {"start", "configure", "tick"}: if fn.__name__ not in eventsequence.Events - {"start", "configure", "tick"}:
raise NotImplementedError( raise NotImplementedError(
"Concurrent decorator not supported for '%s' method." % fn.__name__ "Concurrent decorator not supported for '%s' method." % fn.__name__
) )

View File

@ -3,7 +3,7 @@ import contextlib
import mitmproxy.master import mitmproxy.master
import mitmproxy.options import mitmproxy.options
from mitmproxy import proxy from mitmproxy import proxy
from mitmproxy import events from mitmproxy import eventsequence
from mitmproxy import exceptions from mitmproxy import exceptions
@ -57,7 +57,7 @@ class context:
is taken (as in flow interception). is taken (as in flow interception).
""" """
f.reply._state = "handled" f.reply._state = "handled"
for evt, arg in events.event_sequence(f): for evt, arg in eventsequence.iterate(f):
h = getattr(addon, evt, None) h = getattr(addon, evt, None)
if h: if h:
h(arg) h(arg)

View File

@ -1,5 +1,5 @@
from mitmproxy import controller from mitmproxy import controller
from mitmproxy import events from mitmproxy import eventsequence
from mitmproxy import ctx from mitmproxy import ctx
import sys import sys
@ -11,7 +11,7 @@ class CallLogger:
self.name = name self.name = name
def __getattr__(self, attr): def __getattr__(self, attr):
if attr in events.Events: if attr in eventsequence.Events:
def prox(*args, **kwargs): def prox(*args, **kwargs):
lg = (self.name, attr, args, kwargs) lg = (self.name, attr, args, kwargs)
if attr != "log": if attr != "log":

View File

@ -1,81 +1,57 @@
from mitmproxy import events import pytest
import contextlib
from . import tservers from mitmproxy import eventsequence
from mitmproxy.test import tflow
class Eventer: @pytest.mark.parametrize("resp, err", [
def __init__(self, **handlers): (False, False),
self.failure = None (True, False),
self.called = [] (False, True),
self.handlers = handlers (True, True),
for i in events.Events - {"tick"}: ])
def mkprox(): def test_http_flow(resp, err):
evt = i f = tflow.tflow(resp=resp, err=err)
i = eventsequence.iterate(f)
def prox(*args, **kwargs): assert next(i) == ("requestheaders", f)
self.called.append(evt) assert next(i) == ("request", f)
if evt in self.handlers: if resp:
try: assert next(i) == ("responseheaders", f)
handlers[evt](*args, **kwargs) assert next(i) == ("response", f)
except AssertionError as e: if err:
self.failure = e assert next(i) == ("error", f)
return prox
setattr(self, i, mkprox())
def fail(self):
pass
class SequenceTester: @pytest.mark.parametrize("err", [False, True])
@contextlib.contextmanager def test_websocket_flow(err):
def addon(self, addon): f = tflow.twebsocketflow(err=err)
self.master.addons.add(addon) i = eventsequence.iterate(f)
yield assert next(i) == ("websocket_start", f)
self.master.addons.remove(addon) assert len(f.messages) == 0
if addon.failure: assert next(i) == ("websocket_message", f)
raise addon.failure assert len(f.messages) == 1
assert next(i) == ("websocket_message", f)
assert len(f.messages) == 2
if err:
assert next(i) == ("websocket_error", f)
assert next(i) == ("websocket_end", f)
class TestBasic(tservers.HTTPProxyTest, SequenceTester): @pytest.mark.parametrize("err", [False, True])
ssl = True def test_tcp_flow(err):
f = tflow.ttcpflow(err=err)
i = eventsequence.iterate(f)
assert next(i) == ("tcp_start", f)
assert len(f.messages) == 0
assert next(i) == ("tcp_message", f)
assert len(f.messages) == 1
assert next(i) == ("tcp_message", f)
assert len(f.messages) == 2
if err:
assert next(i) == ("tcp_error", f)
assert next(i) == ("tcp_end", f)
def test_requestheaders(self):
def hdrs(f): def test_invalid():
assert f.request.headers with pytest.raises(ValueError):
assert not f.request.content next(eventsequence.iterate(42))
def req(f):
assert f.request.headers
assert f.request.content
with self.addon(Eventer(requestheaders=hdrs, request=req)):
p = self.pathoc()
with p.connect():
assert p.request("get:'/p/200':b@10").status_code == 200
def test_100_continue_fail(self):
e = Eventer()
with self.addon(e):
p = self.pathoc()
with p.connect():
p.request(
"""
get:'/p/200'
h'expect'='100-continue'
h'content-length'='1000'
da
"""
)
assert "requestheaders" in e.called
assert "responseheaders" not in e.called
def test_connect(self):
e = Eventer()
with self.addon(e):
p = self.pathoc()
with p.connect():
p.request("get:'/p/200:b@1'")
assert "http_connect" in e.called
assert e.called.count("requestheaders") == 1
assert e.called.count("request") == 1

View File

@ -17,7 +17,7 @@ commands =
--no-full-cov=mitmproxy/net/tcp.py --no-full-cov=mitmproxy/net/http/cookies.py --no-full-cov=mitmproxy/net/http/encoding.py --no-full-cov=mitmproxy/net/http/message.py --no-full-cov=mitmproxy/net/http/request.py --no-full-cov=mitmproxy/net/http/response.py --no-full-cov=mitmproxy/net/http/url.py \ --no-full-cov=mitmproxy/net/tcp.py --no-full-cov=mitmproxy/net/http/cookies.py --no-full-cov=mitmproxy/net/http/encoding.py --no-full-cov=mitmproxy/net/http/message.py --no-full-cov=mitmproxy/net/http/request.py --no-full-cov=mitmproxy/net/http/response.py --no-full-cov=mitmproxy/net/http/url.py \
--no-full-cov=mitmproxy/proxy/protocol/ --no-full-cov=mitmproxy/proxy/config.py --no-full-cov=mitmproxy/proxy/root_context.py --no-full-cov=mitmproxy/proxy/server.py \ --no-full-cov=mitmproxy/proxy/protocol/ --no-full-cov=mitmproxy/proxy/config.py --no-full-cov=mitmproxy/proxy/root_context.py --no-full-cov=mitmproxy/proxy/server.py \
--no-full-cov=mitmproxy/tools/ \ --no-full-cov=mitmproxy/tools/ \
--no-full-cov=mitmproxy/certs.py --no-full-cov=mitmproxy/connections.py --no-full-cov=mitmproxy/controller.py --no-full-cov=mitmproxy/events.py --no-full-cov=mitmproxy/export.py --no-full-cov=mitmproxy/flow.py --no-full-cov=mitmproxy/flowfilter.py --no-full-cov=mitmproxy/http.py --no-full-cov=mitmproxy/io_compat.py --no-full-cov=mitmproxy/master.py --no-full-cov=mitmproxy/optmanager.py \ --no-full-cov=mitmproxy/certs.py --no-full-cov=mitmproxy/connections.py --no-full-cov=mitmproxy/controller.py --no-full-cov=mitmproxy/export.py --no-full-cov=mitmproxy/flow.py --no-full-cov=mitmproxy/flowfilter.py --no-full-cov=mitmproxy/http.py --no-full-cov=mitmproxy/io_compat.py --no-full-cov=mitmproxy/master.py --no-full-cov=mitmproxy/optmanager.py \
--full-cov=pathod/ --no-full-cov=pathod/pathoc.py --no-full-cov=pathod/pathod.py --no-full-cov=pathod/test.py --no-full-cov=pathod/protocols/http2.py \ --full-cov=pathod/ --no-full-cov=pathod/pathoc.py --no-full-cov=pathod/pathod.py --no-full-cov=pathod/test.py --no-full-cov=pathod/protocols/http2.py \
{posargs} {posargs}
{env:CI_COMMANDS:python -c ""} {env:CI_COMMANDS:python -c ""}