mirror of
https://github.com/mitmproxy/mitmproxy.git
synced 2024-11-23 13:19:48 +00:00
code formatting: fix whitespace issues
This commit is contained in:
parent
c2bb29f669
commit
8c37538314
@ -372,10 +372,10 @@ def proxy_options(parser):
|
||||
rawtcp = group.add_mutually_exclusive_group()
|
||||
rawtcp.add_argument("--raw-tcp", action="store_true", dest="rawtcp")
|
||||
rawtcp.add_argument("--no-raw-tcp", action="store_false", dest="rawtcp",
|
||||
help="Explicitly enable/disable experimental raw tcp support. "
|
||||
"Disabled by default. "
|
||||
"Default value will change in a future version."
|
||||
)
|
||||
help="Explicitly enable/disable experimental raw tcp support. "
|
||||
"Disabled by default. "
|
||||
"Default value will change in a future version."
|
||||
)
|
||||
|
||||
|
||||
def proxy_ssl_options(parser):
|
||||
|
@ -22,6 +22,7 @@ EVENTLOG_SIZE = 500
|
||||
|
||||
|
||||
class ConsoleState(flow.State):
|
||||
|
||||
def __init__(self):
|
||||
flow.State.__init__(self)
|
||||
self.focus = None
|
||||
@ -732,4 +733,4 @@ class ConsoleMaster(flow.FlowMaster):
|
||||
if super(ConsoleMaster, self).handle_script_change(script):
|
||||
signals.status_message.send(message='"{}" reloaded.'.format(script.filename))
|
||||
else:
|
||||
signals.status_message.send(message='Error reloading "{}".'.format(script.filename))
|
||||
signals.status_message.send(message='Error reloading "{}".'.format(script.filename))
|
||||
|
@ -242,7 +242,7 @@ def ask_save_path(prompt, data):
|
||||
signals.status_prompt_path.send(
|
||||
prompt = prompt,
|
||||
callback = ask_save_overwrite,
|
||||
args = (data, )
|
||||
args = (data, )
|
||||
)
|
||||
|
||||
|
||||
@ -290,16 +290,16 @@ def copy_as_curl_command(flow):
|
||||
data = "curl "
|
||||
|
||||
for k, v in flow.request.headers.fields:
|
||||
data += "-H '%s:%s' " % (k, v)
|
||||
data += "-H '%s:%s' " % (k, v)
|
||||
|
||||
if flow.request.method != "GET":
|
||||
data += "-X %s " % flow.request.method
|
||||
data += "-X %s " % flow.request.method
|
||||
|
||||
full_url = flow.request.scheme + "://" + flow.request.host + flow.request.path
|
||||
data += "'%s'" % full_url
|
||||
|
||||
if flow.request.content:
|
||||
data += " --data-binary '%s'" % flow.request.content
|
||||
data += " --data-binary '%s'" % flow.request.content
|
||||
|
||||
copy_to_clipboard_or_prompt(data)
|
||||
|
||||
@ -316,7 +316,7 @@ def copy_as_python_code(flow):
|
||||
|
||||
headers = "\n"
|
||||
for k, v in flow.request.headers.fields:
|
||||
headers += " '%s': '%s',\n" % (k, v)
|
||||
headers += " '%s': '%s',\n" % (k, v)
|
||||
|
||||
full_url = flow.request.scheme + "://" + flow.request.host + flow.request.path
|
||||
|
||||
@ -439,7 +439,7 @@ flowcache = utils.LRUCache(800)
|
||||
|
||||
|
||||
def format_flow(f, focus, extended=False, hostheader=False, padding=2,
|
||||
marked=False):
|
||||
marked=False):
|
||||
d = dict(
|
||||
intercepted = f.intercepted,
|
||||
acked = f.reply.acked,
|
||||
|
@ -43,6 +43,7 @@ footer = [
|
||||
|
||||
|
||||
class EventListBox(urwid.ListBox):
|
||||
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
urwid.ListBox.__init__(self, master.eventlist)
|
||||
@ -60,6 +61,7 @@ class EventListBox(urwid.ListBox):
|
||||
|
||||
|
||||
class BodyPile(urwid.Pile):
|
||||
|
||||
def __init__(self, master):
|
||||
h = urwid.Text("Event log")
|
||||
h = urwid.Padding(h, align="left", width=("relative", 100))
|
||||
@ -103,6 +105,7 @@ class BodyPile(urwid.Pile):
|
||||
|
||||
|
||||
class ConnectionItem(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, master, state, flow, focus):
|
||||
self.master, self.state, self.flow = master, state, flow
|
||||
self.f = focus
|
||||
@ -273,6 +276,7 @@ class ConnectionItem(urwid.WidgetWrap):
|
||||
|
||||
|
||||
class FlowListWalker(urwid.ListWalker):
|
||||
|
||||
def __init__(self, master, state):
|
||||
self.master, self.state = master, state
|
||||
signals.flowlist_change.connect(self.sig_flowlist_change)
|
||||
@ -301,6 +305,7 @@ class FlowListWalker(urwid.ListWalker):
|
||||
|
||||
|
||||
class FlowListBox(urwid.ListBox):
|
||||
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
urwid.ListBox.__init__(
|
||||
|
@ -96,6 +96,7 @@ footer = [
|
||||
|
||||
|
||||
class FlowViewHeader(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, master, f):
|
||||
self.master, self.flow = master, f
|
||||
self._w = common.format_flow(
|
||||
@ -218,7 +219,7 @@ class FlowView(tabs.Tabs):
|
||||
txt = []
|
||||
for (style, text) in line:
|
||||
if total_chars + len(text) > max_chars:
|
||||
text = text[:max_chars-total_chars]
|
||||
text = text[:max_chars - total_chars]
|
||||
txt.append((style, text))
|
||||
total_chars += len(text)
|
||||
if total_chars == max_chars:
|
||||
|
@ -63,6 +63,7 @@ class TextColumn:
|
||||
|
||||
|
||||
class SubgridColumn:
|
||||
|
||||
def __init__(self, heading, subeditor):
|
||||
self.heading = heading
|
||||
self.subeditor = subeditor
|
||||
@ -97,6 +98,7 @@ class SubgridColumn:
|
||||
|
||||
|
||||
class SEscaped(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, txt):
|
||||
txt = txt.encode("string-escape")
|
||||
w = urwid.Text(txt, wrap="any")
|
||||
@ -113,6 +115,7 @@ class SEscaped(urwid.WidgetWrap):
|
||||
|
||||
|
||||
class SEdit(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, txt):
|
||||
txt = txt.encode("string-escape")
|
||||
w = urwid.Edit(edit_text=txt, wrap="any", multiline=True)
|
||||
@ -127,6 +130,7 @@ class SEdit(urwid.WidgetWrap):
|
||||
|
||||
|
||||
class GridRow(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, focused, editing, editor, values):
|
||||
self.focused, self.editing, self.editor = focused, editing, editor
|
||||
|
||||
@ -172,6 +176,7 @@ class GridRow(urwid.WidgetWrap):
|
||||
|
||||
|
||||
class GridWalker(urwid.ListWalker):
|
||||
|
||||
"""
|
||||
Stores rows as a list of (rows, errors) tuples, where rows is a list
|
||||
and errors is a set with an entry of each offset in rows that is an
|
||||
@ -311,6 +316,7 @@ class GridWalker(urwid.ListWalker):
|
||||
|
||||
|
||||
class GridListBox(urwid.ListBox):
|
||||
|
||||
def __init__(self, lw):
|
||||
urwid.ListBox.__init__(self, lw)
|
||||
|
||||
|
@ -12,6 +12,7 @@ footer = [
|
||||
|
||||
|
||||
class HelpView(urwid.ListBox):
|
||||
|
||||
def __init__(self, help_context):
|
||||
self.help_context = help_context or []
|
||||
urwid.ListBox.__init__(
|
||||
|
@ -22,6 +22,7 @@ help_context = _mkhelp()
|
||||
|
||||
|
||||
class Options(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
self.lb = select.Select(
|
||||
|
@ -18,6 +18,7 @@ help_context = _mkhelp()
|
||||
|
||||
|
||||
class PalettePicker(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
low, high = [], []
|
||||
|
@ -65,6 +65,7 @@ class Palette:
|
||||
|
||||
|
||||
class LowDark(Palette):
|
||||
|
||||
"""
|
||||
Low-color dark background
|
||||
"""
|
||||
@ -129,6 +130,7 @@ class Dark(LowDark):
|
||||
|
||||
|
||||
class LowLight(Palette):
|
||||
|
||||
"""
|
||||
Low-color light background
|
||||
"""
|
||||
|
@ -5,6 +5,7 @@ import urwid
|
||||
|
||||
|
||||
class _PathCompleter:
|
||||
|
||||
def __init__(self, _testing=False):
|
||||
"""
|
||||
_testing: disables reloading of the lookup table to make testing
|
||||
@ -55,6 +56,7 @@ class _PathCompleter:
|
||||
|
||||
|
||||
class PathEdit(urwid.Edit, _PathCompleter):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
urwid.Edit.__init__(self, *args, **kwargs)
|
||||
_PathCompleter.__init__(self)
|
||||
|
@ -4,6 +4,7 @@ from . import signals
|
||||
|
||||
|
||||
class Highlight(urwid.AttrMap):
|
||||
|
||||
def __init__(self, t):
|
||||
urwid.AttrMap.__init__(
|
||||
self,
|
||||
@ -14,6 +15,7 @@ class Highlight(urwid.AttrMap):
|
||||
|
||||
|
||||
class Searchable(urwid.ListBox):
|
||||
|
||||
def __init__(self, state, contents):
|
||||
self.walker = urwid.SimpleFocusListWalker(contents)
|
||||
urwid.ListBox.__init__(self, self.walker)
|
||||
|
@ -4,6 +4,7 @@ from . import common
|
||||
|
||||
|
||||
class _OptionWidget(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, option, text, shortcut, active, focus):
|
||||
self.option = option
|
||||
textattr = "text"
|
||||
@ -36,6 +37,7 @@ class _OptionWidget(urwid.WidgetWrap):
|
||||
|
||||
|
||||
class OptionWalker(urwid.ListWalker):
|
||||
|
||||
def __init__(self, options):
|
||||
urwid.ListWalker.__init__(self)
|
||||
self.options = options
|
||||
@ -59,6 +61,7 @@ class OptionWalker(urwid.ListWalker):
|
||||
|
||||
|
||||
class Heading:
|
||||
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
@ -73,6 +76,7 @@ _neg = lambda: False
|
||||
|
||||
|
||||
class Option:
|
||||
|
||||
def __init__(self, text, shortcut, getstate=None, activate=None):
|
||||
self.text = text
|
||||
self.shortcut = shortcut
|
||||
@ -89,6 +93,7 @@ class Option:
|
||||
|
||||
|
||||
class Select(urwid.ListBox):
|
||||
|
||||
def __init__(self, options):
|
||||
self.walker = OptionWalker(options)
|
||||
urwid.ListBox.__init__(
|
||||
|
@ -2,6 +2,8 @@ import blinker
|
||||
|
||||
# Show a status message in the action bar
|
||||
sig_add_event = blinker.Signal()
|
||||
|
||||
|
||||
def add_event(e, level):
|
||||
sig_add_event.send(
|
||||
None,
|
||||
|
@ -7,6 +7,7 @@ from . import pathedit, signals, common
|
||||
|
||||
|
||||
class ActionBar(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self):
|
||||
urwid.WidgetWrap.__init__(self, None)
|
||||
self.clear()
|
||||
@ -108,6 +109,7 @@ class ActionBar(urwid.WidgetWrap):
|
||||
|
||||
|
||||
class StatusBar(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, master, helptext):
|
||||
self.master, self.helptext = master, helptext
|
||||
self.ab = ActionBar()
|
||||
|
@ -2,6 +2,7 @@ import urwid
|
||||
|
||||
|
||||
class Tab(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, offset, content, attr, onclick):
|
||||
"""
|
||||
onclick is called on click with the tab offset as argument
|
||||
@ -20,6 +21,7 @@ class Tab(urwid.WidgetWrap):
|
||||
|
||||
|
||||
class Tabs(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, tabs, tab_offset=0):
|
||||
urwid.WidgetWrap.__init__(self, "")
|
||||
self.tab_offset = tab_offset
|
||||
|
@ -3,6 +3,7 @@ from . import signals
|
||||
|
||||
|
||||
class Window(urwid.Frame):
|
||||
|
||||
def __init__(self, master, body, header, footer, helpctx):
|
||||
urwid.Frame.__init__(
|
||||
self,
|
||||
|
@ -275,6 +275,7 @@ class ViewMultipart(View):
|
||||
|
||||
if pyamf:
|
||||
class DummyObject(dict):
|
||||
|
||||
def __init__(self, alias):
|
||||
dict.__init__(self)
|
||||
|
||||
@ -282,7 +283,6 @@ if pyamf:
|
||||
data = input.readObject()
|
||||
self["data"] = data
|
||||
|
||||
|
||||
def pyamf_class_loader(s):
|
||||
for i in pyamf.CLASS_LOADERS:
|
||||
if i != pyamf_class_loader:
|
||||
@ -291,10 +291,8 @@ if pyamf:
|
||||
return v
|
||||
return DummyObject
|
||||
|
||||
|
||||
pyamf.register_class_loader(pyamf_class_loader)
|
||||
|
||||
|
||||
class ViewAMF(View):
|
||||
name = "AMF"
|
||||
prompt = ("amf", "f")
|
||||
@ -417,6 +415,7 @@ class ViewImage(View):
|
||||
|
||||
|
||||
class ViewProtobuf(View):
|
||||
|
||||
"""Human friendly view of protocol buffers
|
||||
The view uses the protoc compiler to decode the binary
|
||||
"""
|
||||
|
@ -4,6 +4,7 @@ import threading
|
||||
|
||||
|
||||
class DummyReply:
|
||||
|
||||
"""
|
||||
A reply object that does nothing. Useful when we need an object to seem
|
||||
like it has a channel, and during testing.
|
||||
@ -17,6 +18,7 @@ class DummyReply:
|
||||
|
||||
|
||||
class Reply:
|
||||
|
||||
"""
|
||||
Messages sent through a channel are decorated with a "reply" attribute.
|
||||
This object is used to respond to the message through the return
|
||||
@ -38,6 +40,7 @@ class Reply:
|
||||
|
||||
|
||||
class Channel:
|
||||
|
||||
def __init__(self, q, should_exit):
|
||||
self.q = q
|
||||
self.should_exit = should_exit
|
||||
@ -67,6 +70,7 @@ class Channel:
|
||||
|
||||
|
||||
class Slave(threading.Thread):
|
||||
|
||||
"""
|
||||
Slaves get a channel end-point through which they can send messages to
|
||||
the master.
|
||||
@ -84,6 +88,7 @@ class Slave(threading.Thread):
|
||||
|
||||
|
||||
class Master(object):
|
||||
|
||||
"""
|
||||
Masters get and respond to messages from slaves.
|
||||
"""
|
||||
|
@ -9,6 +9,7 @@ import netlib.utils
|
||||
from . import flow, filt, contentviews
|
||||
from .exceptions import ContentViewException
|
||||
|
||||
|
||||
class DumpError(Exception):
|
||||
pass
|
||||
|
||||
@ -55,6 +56,7 @@ class Options(object):
|
||||
|
||||
|
||||
class DumpMaster(flow.FlowMaster):
|
||||
|
||||
def __init__(self, server, options, outfile=None):
|
||||
flow.FlowMaster.__init__(self, server, flow.State())
|
||||
self.outfile = outfile
|
||||
@ -168,7 +170,7 @@ class DumpMaster(flow.FlowMaster):
|
||||
"{}: {}".format(
|
||||
click.style(k, fg="blue", bold=True),
|
||||
click.style(v, fg="blue"))
|
||||
for k, v in message.headers.fields
|
||||
for k, v in message.headers.fields
|
||||
)
|
||||
self.echo(headers, indent=4)
|
||||
if self.o.flow_detail >= 3:
|
||||
@ -234,7 +236,7 @@ class DumpMaster(flow.FlowMaster):
|
||||
client = click.style("[replay]", fg="yellow", bold=True)
|
||||
|
||||
method = flow.request.method
|
||||
method_color=dict(
|
||||
method_color = dict(
|
||||
GET="green",
|
||||
DELETE="red"
|
||||
).get(method.upper(), "magenta")
|
||||
|
@ -9,9 +9,11 @@ from __future__ import (absolute_import, print_function, division)
|
||||
|
||||
|
||||
class ProxyException(Exception):
|
||||
|
||||
"""
|
||||
Base class for all exceptions thrown by libmproxy.
|
||||
"""
|
||||
|
||||
def __init__(self, message=None):
|
||||
super(ProxyException, self).__init__(message)
|
||||
|
||||
@ -25,6 +27,7 @@ class TlsProtocolException(ProtocolException):
|
||||
|
||||
|
||||
class ClientHandshakeException(TlsProtocolException):
|
||||
|
||||
def __init__(self, message, server):
|
||||
super(ClientHandshakeException, self).__init__(message)
|
||||
self.server = server
|
||||
@ -51,4 +54,4 @@ class ReplayException(ProxyException):
|
||||
|
||||
|
||||
class ScriptException(ProxyException):
|
||||
pass
|
||||
pass
|
||||
|
@ -38,6 +38,7 @@ import pyparsing as pp
|
||||
|
||||
|
||||
class _Token:
|
||||
|
||||
def dump(self, indent=0, fp=sys.stdout):
|
||||
print >> fp, "\t" * indent, self.__class__.__name__,
|
||||
if hasattr(self, "expr"):
|
||||
@ -46,6 +47,7 @@ class _Token:
|
||||
|
||||
|
||||
class _Action(_Token):
|
||||
|
||||
@classmethod
|
||||
def make(klass, s, loc, toks):
|
||||
return klass(*toks[1:])
|
||||
@ -261,6 +263,7 @@ class FDst(_Rex):
|
||||
|
||||
|
||||
class _Int(_Action):
|
||||
|
||||
def __init__(self, num):
|
||||
self.num = int(num)
|
||||
|
||||
@ -275,6 +278,7 @@ class FCode(_Int):
|
||||
|
||||
|
||||
class FAnd(_Token):
|
||||
|
||||
def __init__(self, lst):
|
||||
self.lst = lst
|
||||
|
||||
@ -288,6 +292,7 @@ class FAnd(_Token):
|
||||
|
||||
|
||||
class FOr(_Token):
|
||||
|
||||
def __init__(self, lst):
|
||||
self.lst = lst
|
||||
|
||||
@ -301,6 +306,7 @@ class FOr(_Token):
|
||||
|
||||
|
||||
class FNot(_Token):
|
||||
|
||||
def __init__(self, itm):
|
||||
self.itm = itm[0]
|
||||
|
||||
|
@ -22,6 +22,7 @@ from .models import ClientConnection, ServerConnection, HTTPResponse, HTTPFlow,
|
||||
|
||||
|
||||
class AppRegistry:
|
||||
|
||||
def __init__(self):
|
||||
self.apps = {}
|
||||
|
||||
@ -49,6 +50,7 @@ class AppRegistry:
|
||||
|
||||
|
||||
class ReplaceHooks:
|
||||
|
||||
def __init__(self):
|
||||
self.lst = []
|
||||
|
||||
@ -101,6 +103,7 @@ class ReplaceHooks:
|
||||
|
||||
|
||||
class SetHeaders:
|
||||
|
||||
def __init__(self):
|
||||
self.lst = []
|
||||
|
||||
@ -155,6 +158,7 @@ class SetHeaders:
|
||||
|
||||
|
||||
class StreamLargeBodies(object):
|
||||
|
||||
def __init__(self, max_size):
|
||||
self.max_size = max_size
|
||||
|
||||
@ -169,6 +173,7 @@ class StreamLargeBodies(object):
|
||||
|
||||
|
||||
class ClientPlaybackState:
|
||||
|
||||
def __init__(self, flows, exit):
|
||||
self.flows, self.exit = flows, exit
|
||||
self.current = None
|
||||
@ -203,6 +208,7 @@ class ClientPlaybackState:
|
||||
|
||||
|
||||
class ServerPlaybackState:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
headers,
|
||||
@ -295,6 +301,7 @@ class ServerPlaybackState:
|
||||
|
||||
|
||||
class StickyCookieState:
|
||||
|
||||
def __init__(self, flt):
|
||||
"""
|
||||
flt: Compiled filter.
|
||||
@ -342,10 +349,11 @@ class StickyCookieState:
|
||||
l.append(self.jar[i].output(header="").strip())
|
||||
if l:
|
||||
f.request.stickycookie = True
|
||||
f.request.headers.set_all("cookie",l)
|
||||
f.request.headers.set_all("cookie", l)
|
||||
|
||||
|
||||
class StickyAuthState:
|
||||
|
||||
def __init__(self, flt):
|
||||
"""
|
||||
flt: Compiled filter.
|
||||
@ -397,6 +405,7 @@ class FlowList(object):
|
||||
|
||||
|
||||
class FlowView(FlowList):
|
||||
|
||||
def __init__(self, store, filt=None):
|
||||
self._list = []
|
||||
if not filt:
|
||||
@ -433,6 +442,7 @@ class FlowView(FlowList):
|
||||
|
||||
|
||||
class FlowStore(FlowList):
|
||||
|
||||
"""
|
||||
Responsible for handling flows in the state:
|
||||
Keeps a list of all flows and provides views on them.
|
||||
@ -526,6 +536,7 @@ class FlowStore(FlowList):
|
||||
|
||||
|
||||
class State(object):
|
||||
|
||||
def __init__(self):
|
||||
self.flows = FlowStore()
|
||||
self.view = FlowView(self.flows, None)
|
||||
@ -613,6 +624,7 @@ class State(object):
|
||||
|
||||
|
||||
class FlowMaster(controller.Master):
|
||||
|
||||
def __init__(self, server, state):
|
||||
controller.Master.__init__(self, server)
|
||||
self.state = state
|
||||
@ -1099,6 +1111,7 @@ def read_flows_from_paths(paths):
|
||||
|
||||
|
||||
class FlowWriter:
|
||||
|
||||
def __init__(self, fo):
|
||||
self.fo = fo
|
||||
|
||||
@ -1108,12 +1121,14 @@ class FlowWriter:
|
||||
|
||||
|
||||
class FlowReadError(Exception):
|
||||
|
||||
@property
|
||||
def strerror(self):
|
||||
return self.args[0]
|
||||
|
||||
|
||||
class FlowReader:
|
||||
|
||||
def __init__(self, fo):
|
||||
self.fo = fo
|
||||
|
||||
@ -1139,6 +1154,7 @@ class FlowReader:
|
||||
|
||||
|
||||
class FilteredFlowWriter:
|
||||
|
||||
def __init__(self, fo, filt):
|
||||
self.fo = fo
|
||||
self.filt = filt
|
||||
|
@ -8,6 +8,7 @@ from .. import stateobject, utils
|
||||
|
||||
|
||||
class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
|
||||
|
||||
def __init__(self, client_connection, address, server):
|
||||
# Eventually, this object is restored from state. We don't have a
|
||||
# connection then.
|
||||
@ -88,6 +89,7 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
|
||||
|
||||
|
||||
class ServerConnection(tcp.TCPClient, stateobject.StateObject):
|
||||
|
||||
def __init__(self, address, source_address=None):
|
||||
tcp.TCPClient.__init__(self, address, source_address)
|
||||
|
||||
@ -134,7 +136,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
|
||||
d = super(ServerConnection, self).get_state(short)
|
||||
d.update(
|
||||
address=({"address": self.address(),
|
||||
"use_ipv6": self.address.use_ipv6} if self.address else {}),
|
||||
"use_ipv6": self.address.use_ipv6} if self.address else {}),
|
||||
source_address=({"address": self.source_address(),
|
||||
"use_ipv6": self.source_address.use_ipv6} if self.source_address else None),
|
||||
cert=self.cert.to_pem() if self.cert else None
|
||||
|
@ -7,6 +7,7 @@ from .connections import ClientConnection, ServerConnection
|
||||
|
||||
|
||||
class Error(stateobject.StateObject):
|
||||
|
||||
"""
|
||||
An Error.
|
||||
|
||||
@ -53,6 +54,7 @@ class Error(stateobject.StateObject):
|
||||
|
||||
|
||||
class Flow(stateobject.StateObject):
|
||||
|
||||
"""
|
||||
A Flow is a collection of objects representing a single transaction.
|
||||
This class is usually subclassed for each protocol, e.g. HTTPFlow.
|
||||
|
@ -102,6 +102,7 @@ class MessageMixin(stateobject.StateObject):
|
||||
|
||||
|
||||
class HTTPRequest(MessageMixin, Request):
|
||||
|
||||
"""
|
||||
An HTTP request.
|
||||
|
||||
@ -264,6 +265,7 @@ class HTTPRequest(MessageMixin, Request):
|
||||
|
||||
|
||||
class HTTPResponse(MessageMixin, Response):
|
||||
|
||||
"""
|
||||
An HTTP response.
|
||||
|
||||
@ -411,6 +413,7 @@ class HTTPResponse(MessageMixin, Response):
|
||||
|
||||
|
||||
class HTTPFlow(Flow):
|
||||
|
||||
"""
|
||||
A HTTPFlow is a collection of objects representing a single HTTP
|
||||
transaction.
|
||||
@ -544,4 +547,4 @@ def make_connect_response(http_version):
|
||||
"",
|
||||
)
|
||||
|
||||
expect_continue_response = HTTPResponse(b"HTTP/1.1", 100, "Continue", Headers(), b"")
|
||||
expect_continue_response = HTTPResponse(b"HTTP/1.1", 100, "Continue", Headers(), b"")
|
||||
|
@ -15,6 +15,7 @@ class Adapter(tornado.wsgi.WSGIAdapter):
|
||||
# Tornado doesn't make the WSGI environment available to pages, so this
|
||||
# hideous monkey patch is the easiest way to get to the mitmproxy.master
|
||||
# variable.
|
||||
|
||||
def __init__(self, application):
|
||||
self._application = application
|
||||
|
||||
@ -32,12 +33,14 @@ class Adapter(tornado.wsgi.WSGIAdapter):
|
||||
|
||||
|
||||
class Index(tornado.web.RequestHandler):
|
||||
|
||||
def get(self):
|
||||
t = loader.load("index.html")
|
||||
self.write(t.generate())
|
||||
|
||||
|
||||
class PEM(tornado.web.RequestHandler):
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
return config.CONF_BASENAME + "-ca-cert.pem"
|
||||
@ -55,6 +58,7 @@ class PEM(tornado.web.RequestHandler):
|
||||
|
||||
|
||||
class P12(tornado.web.RequestHandler):
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
return config.CONF_BASENAME + "-ca-cert.p12"
|
||||
|
@ -6,6 +6,7 @@ SO_ORIGINAL_DST = 80
|
||||
|
||||
|
||||
class Resolver(object):
|
||||
|
||||
def original_addr(self, csock):
|
||||
odestdata = csock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
|
||||
_, port, a1, a2, a3, a4 = struct.unpack("!HHBBBBxxxxxxxx", odestdata)
|
||||
|
@ -18,6 +18,7 @@ PROXY_API_PORT = 8085
|
||||
|
||||
|
||||
class Resolver(object):
|
||||
|
||||
def __init__(self):
|
||||
TransparentProxy.setup()
|
||||
self.socket = None
|
||||
@ -53,6 +54,7 @@ class Resolver(object):
|
||||
|
||||
|
||||
class APIRequestHandler(SocketServer.StreamRequestHandler):
|
||||
|
||||
"""
|
||||
TransparentProxy API: Returns the pickled server address, port tuple
|
||||
for each received pickled client address, port tuple.
|
||||
@ -77,6 +79,7 @@ class APIRequestHandler(SocketServer.StreamRequestHandler):
|
||||
|
||||
|
||||
class APIServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
|
||||
|
||||
def __init__(self, proxifier, *args, **kwargs):
|
||||
SocketServer.TCPServer.__init__(self, *args, **kwargs)
|
||||
self.proxifier = proxifier
|
||||
@ -110,6 +113,7 @@ def MIB_TCPTABLE2(size):
|
||||
|
||||
|
||||
class TransparentProxy(object):
|
||||
|
||||
"""
|
||||
Transparent Windows Proxy for mitmproxy based on WinDivert/PyDivert.
|
||||
|
||||
|
@ -9,6 +9,7 @@ from netlib.exceptions import TcpException
|
||||
|
||||
|
||||
class _LayerCodeCompletion(object):
|
||||
|
||||
"""
|
||||
Dummy class that provides type hinting in PyCharm, which simplifies development a lot.
|
||||
"""
|
||||
@ -30,6 +31,7 @@ class _LayerCodeCompletion(object):
|
||||
|
||||
|
||||
class Layer(_LayerCodeCompletion):
|
||||
|
||||
"""
|
||||
Base class for all layers. All other protocol layers should inherit from this class.
|
||||
"""
|
||||
@ -90,6 +92,7 @@ class Layer(_LayerCodeCompletion):
|
||||
|
||||
|
||||
class ServerConnectionMixin(object):
|
||||
|
||||
"""
|
||||
Mixin that provides a layer with the capabilities to manage a server connection.
|
||||
The server address can be passed in the constructor or set by calling :py:meth:`set_server`.
|
||||
@ -189,6 +192,7 @@ class ServerConnectionMixin(object):
|
||||
|
||||
|
||||
class Kill(Exception):
|
||||
|
||||
"""
|
||||
Signal that both client and server connection(s) should be killed immediately.
|
||||
"""
|
||||
|
@ -72,6 +72,7 @@ class _StreamingHttpLayer(_HttpLayer):
|
||||
|
||||
|
||||
class Http1Layer(_StreamingHttpLayer):
|
||||
|
||||
def __init__(self, ctx, mode):
|
||||
super(Http1Layer, self).__init__(ctx)
|
||||
self.mode = mode
|
||||
@ -132,6 +133,7 @@ class Http1Layer(_StreamingHttpLayer):
|
||||
|
||||
# TODO: The HTTP2 layer is missing multiplexing, which requires a major rewrite.
|
||||
class Http2Layer(_HttpLayer):
|
||||
|
||||
def __init__(self, ctx, mode):
|
||||
super(Http2Layer, self).__init__(ctx)
|
||||
self.mode = mode
|
||||
@ -229,6 +231,7 @@ class Http2Layer(_HttpLayer):
|
||||
|
||||
|
||||
class ConnectServerConnection(object):
|
||||
|
||||
"""
|
||||
"Fake" ServerConnection to represent state after a CONNECT request to an upstream proxy.
|
||||
"""
|
||||
@ -249,6 +252,7 @@ class ConnectServerConnection(object):
|
||||
|
||||
|
||||
class UpstreamConnectLayer(Layer):
|
||||
|
||||
def __init__(self, ctx, connect_request):
|
||||
super(UpstreamConnectLayer, self).__init__(ctx)
|
||||
self.connect_request = connect_request
|
||||
@ -293,6 +297,7 @@ class UpstreamConnectLayer(Layer):
|
||||
|
||||
|
||||
class HttpLayer(Layer):
|
||||
|
||||
def __init__(self, ctx, mode):
|
||||
super(HttpLayer, self).__init__(ctx)
|
||||
self.mode = mode
|
||||
@ -328,7 +333,8 @@ class HttpLayer(Layer):
|
||||
return
|
||||
except NetlibException as e:
|
||||
self.send_error_response(400, repr(e))
|
||||
six.reraise(ProtocolException, ProtocolException("Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
|
||||
six.reraise(ProtocolException, ProtocolException(
|
||||
"Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
|
||||
|
||||
try:
|
||||
flow = HTTPFlow(self.client_conn, self.server_conn, live=self)
|
||||
@ -376,7 +382,8 @@ class HttpLayer(Layer):
|
||||
self.log(traceback.format_exc(), "debug")
|
||||
return
|
||||
else:
|
||||
six.reraise(ProtocolException, ProtocolException("Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
|
||||
six.reraise(ProtocolException, ProtocolException(
|
||||
"Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
|
||||
finally:
|
||||
flow.live = False
|
||||
|
||||
|
@ -13,6 +13,7 @@ from .base import Layer
|
||||
|
||||
|
||||
class TcpMessage(object):
|
||||
|
||||
def __init__(self, client_conn, server_conn, sender, receiver, message):
|
||||
self.client_conn = client_conn
|
||||
self.server_conn = server_conn
|
||||
|
@ -13,7 +13,6 @@ from ..exceptions import ProtocolException, TlsProtocolException, ClientHandshak
|
||||
from .base import Layer
|
||||
|
||||
|
||||
|
||||
# taken from https://testssl.sh/openssl-rfc.mappping.html
|
||||
CIPHER_ID_NAME_MAP = {
|
||||
0x00: 'NULL-MD5',
|
||||
@ -222,6 +221,7 @@ def is_tls_record_magic(d):
|
||||
d[2] in ('\x00', '\x01', '\x02', '\x03')
|
||||
)
|
||||
|
||||
|
||||
def get_client_hello(client_conn):
|
||||
"""
|
||||
Peek into the socket and read all records that contain the initial client hello message.
|
||||
@ -248,7 +248,9 @@ def get_client_hello(client_conn):
|
||||
client_hello_size = struct.unpack("!I", '\x00' + client_hello[1:4])[0] + 4
|
||||
return client_hello
|
||||
|
||||
|
||||
class TlsClientHello(object):
|
||||
|
||||
def __init__(self, raw_client_hello):
|
||||
self._client_hello = ClientHello.parse(raw_client_hello)
|
||||
|
||||
@ -289,15 +291,16 @@ class TlsClientHello(object):
|
||||
try:
|
||||
return cls(raw_client_hello)
|
||||
except ConstructError as e:
|
||||
raise TlsProtocolException('Cannot parse Client Hello: %s, Raw Client Hello: %s' % \
|
||||
(repr(e), raw_client_hello.encode("hex")))
|
||||
raise TlsProtocolException('Cannot parse Client Hello: %s, Raw Client Hello: %s' %
|
||||
(repr(e), raw_client_hello.encode("hex")))
|
||||
|
||||
def __repr__(self):
|
||||
return "TlsClientHello( sni: %s alpn_protocols: %s, cipher_suites: %s)" % \
|
||||
(self.client_sni, self.client_alpn_protocols, self.client_cipher_suites)
|
||||
(self.client_sni, self.client_alpn_protocols, self.client_cipher_suites)
|
||||
|
||||
|
||||
class TlsLayer(Layer):
|
||||
|
||||
def __init__(self, ctx, client_tls, server_tls):
|
||||
self.client_sni = None
|
||||
self.client_alpn_protocols = None
|
||||
@ -356,7 +359,6 @@ class TlsLayer(Layer):
|
||||
else:
|
||||
return "TlsLayer(inactive)"
|
||||
|
||||
|
||||
def _parse_client_hello(self):
|
||||
"""
|
||||
Peek into the connection, read the initial client hello and parse it to obtain ALPN values.
|
||||
@ -365,7 +367,7 @@ class TlsLayer(Layer):
|
||||
parsed = TlsClientHello.from_client_conn(self.client_conn)
|
||||
self.client_sni = parsed.client_sni
|
||||
self.client_alpn_protocols = parsed.client_alpn_protocols
|
||||
self.client_ciphers = parsed.client_cipher_suites
|
||||
self.client_ciphers = parsed.client_cipher_suites
|
||||
except TlsProtocolException as e:
|
||||
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
|
||||
|
||||
@ -468,7 +470,7 @@ class TlsLayer(Layer):
|
||||
alpn = [x for x in self.client_alpn_protocols if not deprecated_http2_variant(x)]
|
||||
else:
|
||||
alpn = None
|
||||
if alpn and "h2" in alpn and not self.config.http2 :
|
||||
if alpn and "h2" in alpn and not self.config.http2:
|
||||
alpn.remove("h2")
|
||||
|
||||
ciphers_server = self.config.ciphers_server
|
||||
|
@ -19,6 +19,7 @@ DEFAULT_CLIENT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA
|
||||
|
||||
|
||||
class HostMatcher(object):
|
||||
|
||||
def __init__(self, patterns=tuple()):
|
||||
self.patterns = list(patterns)
|
||||
self.regexes = [re.compile(p, re.IGNORECASE) for p in self.patterns]
|
||||
@ -41,6 +42,7 @@ ServerSpec = collections.namedtuple("ServerSpec", "scheme address")
|
||||
|
||||
|
||||
class ProxyConfig:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
host='',
|
||||
|
@ -4,6 +4,7 @@ from ...protocol import Layer, ServerConnectionMixin
|
||||
|
||||
|
||||
class HttpProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __call__(self):
|
||||
layer = self.ctx.next_layer(self)
|
||||
try:
|
||||
@ -14,6 +15,7 @@ class HttpProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
|
||||
class HttpUpstreamProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __init__(self, ctx, server_address):
|
||||
super(HttpUpstreamProxy, self).__init__(ctx, server_address=server_address)
|
||||
|
||||
|
@ -4,6 +4,7 @@ from ...protocol import Layer, ServerConnectionMixin
|
||||
|
||||
|
||||
class ReverseProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __init__(self, ctx, server_address, server_tls):
|
||||
super(ReverseProxy, self).__init__(ctx, server_address=server_address)
|
||||
self.server_tls = server_tls
|
||||
|
@ -8,6 +8,7 @@ from ...protocol import Layer, ServerConnectionMixin
|
||||
|
||||
|
||||
class Socks5Proxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __init__(self, ctx):
|
||||
super(Socks5Proxy, self).__init__(ctx)
|
||||
|
||||
|
@ -6,6 +6,7 @@ from ...protocol import Layer, ServerConnectionMixin
|
||||
|
||||
|
||||
class TransparentProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __init__(self, ctx):
|
||||
super(TransparentProxy, self).__init__(ctx)
|
||||
self.resolver = platform.resolver()
|
||||
|
@ -12,7 +12,9 @@ from ..protocol import (
|
||||
)
|
||||
from .modes import HttpProxy, HttpUpstreamProxy, ReverseProxy
|
||||
|
||||
|
||||
class RootContext(object):
|
||||
|
||||
"""
|
||||
The outermost context provided to the root layer.
|
||||
As a consequence, every layer has access to methods and attributes defined here.
|
||||
@ -131,6 +133,7 @@ class RootContext(object):
|
||||
|
||||
|
||||
class Log(object):
|
||||
|
||||
def __init__(self, msg, level="info"):
|
||||
self.msg = msg
|
||||
self.level = level
|
||||
|
@ -65,6 +65,7 @@ class ProxyServer(tcp.TCPServer):
|
||||
|
||||
|
||||
class ConnectionHandler(object):
|
||||
|
||||
def __init__(self, client_conn, client_address, config, channel):
|
||||
self.config = config
|
||||
"""@type: libmproxy.proxy.config.ProxyConfig"""
|
||||
|
@ -10,4 +10,4 @@ __all__ = [
|
||||
"concurrent",
|
||||
"ScriptException",
|
||||
"reloader"
|
||||
]
|
||||
]
|
||||
|
@ -7,6 +7,7 @@ import threading
|
||||
|
||||
|
||||
class ReplyProxy(object):
|
||||
|
||||
def __init__(self, original_reply, script_thread):
|
||||
self.original_reply = original_reply
|
||||
self.script_thread = script_thread
|
||||
|
@ -1,11 +1,11 @@
|
||||
import os
|
||||
import sys
|
||||
from watchdog.events import RegexMatchingEventHandler
|
||||
from watchdog.events import RegexMatchingEventHandler
|
||||
if sys.platform == 'darwin':
|
||||
from watchdog.observers.polling import PollingObserver as Observer
|
||||
else:
|
||||
from watchdog.observers import Observer
|
||||
# The OSX reloader in watchdog 0.8.3 breaks when unobserving paths.
|
||||
# The OSX reloader in watchdog 0.8.3 breaks when unobserving paths.
|
||||
# We use the PollingObserver instead.
|
||||
|
||||
_observers = {}
|
||||
@ -31,11 +31,12 @@ def unwatch(script):
|
||||
|
||||
|
||||
class _ScriptModificationHandler(RegexMatchingEventHandler):
|
||||
|
||||
def __init__(self, callback, filename='.*'):
|
||||
|
||||
super(_ScriptModificationHandler, self).__init__(
|
||||
ignore_directories=True,
|
||||
regexes=['.*'+filename]
|
||||
regexes=['.*' + filename]
|
||||
)
|
||||
self.callback = callback
|
||||
|
||||
@ -43,4 +44,3 @@ class _ScriptModificationHandler(RegexMatchingEventHandler):
|
||||
self.callback()
|
||||
|
||||
__all__ = ["watch", "unwatch"]
|
||||
|
||||
|
@ -12,6 +12,7 @@ from ..exceptions import ScriptException
|
||||
|
||||
|
||||
class Script(object):
|
||||
|
||||
"""
|
||||
Script object representing an inline script.
|
||||
"""
|
||||
@ -94,4 +95,4 @@ class Script(object):
|
||||
except Exception as e:
|
||||
raise ScriptException(traceback.format_exc(e))
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
@ -6,6 +6,7 @@ from .. import contentviews
|
||||
|
||||
|
||||
class ScriptContext(object):
|
||||
|
||||
"""
|
||||
The script context should be used to interact with the global mitmproxy state from within a
|
||||
script.
|
||||
|
@ -2,6 +2,7 @@ from __future__ import absolute_import
|
||||
|
||||
|
||||
class StateObject(object):
|
||||
|
||||
"""
|
||||
An object with serializable state.
|
||||
|
||||
|
@ -37,7 +37,7 @@ def isBin(s):
|
||||
|
||||
def isMostlyBin(s):
|
||||
s = s[:100]
|
||||
return sum(isBin(ch) for ch in s)/len(s) > 0.3
|
||||
return sum(isBin(ch) for ch in s) / len(s) > 0.3
|
||||
|
||||
|
||||
def isXML(s):
|
||||
@ -73,6 +73,7 @@ def pretty_duration(secs):
|
||||
|
||||
|
||||
class Data:
|
||||
|
||||
def __init__(self, name):
|
||||
m = __import__(name)
|
||||
dirname, _ = os.path.split(m.__file__)
|
||||
@ -93,6 +94,7 @@ pkg_data = Data(__name__)
|
||||
|
||||
|
||||
class LRUCache:
|
||||
|
||||
"""
|
||||
A simple LRU cache for generated values.
|
||||
"""
|
||||
|
@ -11,6 +11,7 @@ class Stop(Exception):
|
||||
|
||||
|
||||
class WebFlowView(flow.FlowView):
|
||||
|
||||
def __init__(self, store):
|
||||
super(WebFlowView, self).__init__(store, None)
|
||||
|
||||
@ -47,6 +48,7 @@ class WebFlowView(flow.FlowView):
|
||||
|
||||
|
||||
class WebState(flow.State):
|
||||
|
||||
def __init__(self):
|
||||
super(WebState, self).__init__()
|
||||
self.view._close()
|
||||
@ -121,6 +123,7 @@ class Options(object):
|
||||
|
||||
|
||||
class WebMaster(flow.FlowMaster):
|
||||
|
||||
def __init__(self, server, options):
|
||||
self.options = options
|
||||
super(WebMaster, self).__init__(server, WebState())
|
||||
|
@ -12,6 +12,7 @@ class APIError(tornado.web.HTTPError):
|
||||
|
||||
|
||||
class RequestHandler(tornado.web.RequestHandler):
|
||||
|
||||
def set_default_headers(self):
|
||||
super(RequestHandler, self).set_default_headers()
|
||||
self.set_header("Server", version.NAMEVERSION)
|
||||
@ -56,12 +57,14 @@ class RequestHandler(tornado.web.RequestHandler):
|
||||
|
||||
|
||||
class IndexHandler(RequestHandler):
|
||||
|
||||
def get(self):
|
||||
_ = self.xsrf_token # https://github.com/tornadoweb/tornado/issues/645
|
||||
self.render("index.html")
|
||||
|
||||
|
||||
class FiltHelp(RequestHandler):
|
||||
|
||||
def get(self):
|
||||
self.write(dict(
|
||||
commands=filt.help
|
||||
@ -94,6 +97,7 @@ class ClientConnection(WebSocketEventBroadcaster):
|
||||
|
||||
|
||||
class Flows(RequestHandler):
|
||||
|
||||
def get(self):
|
||||
self.write(dict(
|
||||
data=[f.get_state(short=True) for f in self.state.flows]
|
||||
@ -101,21 +105,25 @@ class Flows(RequestHandler):
|
||||
|
||||
|
||||
class ClearAll(RequestHandler):
|
||||
|
||||
def post(self):
|
||||
self.state.clear()
|
||||
|
||||
|
||||
class AcceptFlows(RequestHandler):
|
||||
|
||||
def post(self):
|
||||
self.state.flows.accept_all(self.master)
|
||||
|
||||
|
||||
class AcceptFlow(RequestHandler):
|
||||
|
||||
def post(self, flow_id):
|
||||
self.flow.accept_intercept(self.master)
|
||||
|
||||
|
||||
class FlowHandler(RequestHandler):
|
||||
|
||||
def delete(self, flow_id):
|
||||
self.flow.kill(self.master)
|
||||
self.state.delete_flow(self.flow)
|
||||
@ -156,16 +164,19 @@ class FlowHandler(RequestHandler):
|
||||
|
||||
|
||||
class DuplicateFlow(RequestHandler):
|
||||
|
||||
def post(self, flow_id):
|
||||
self.master.duplicate_flow(self.flow)
|
||||
|
||||
|
||||
class RevertFlow(RequestHandler):
|
||||
|
||||
def post(self, flow_id):
|
||||
self.state.revert(self.flow)
|
||||
|
||||
|
||||
class ReplayFlow(RequestHandler):
|
||||
|
||||
def post(self, flow_id):
|
||||
self.flow.backup()
|
||||
self.flow.response = None
|
||||
@ -177,6 +188,7 @@ class ReplayFlow(RequestHandler):
|
||||
|
||||
|
||||
class FlowContent(RequestHandler):
|
||||
|
||||
def get(self, flow_id, message):
|
||||
message = getattr(self.flow, message)
|
||||
|
||||
@ -207,6 +219,7 @@ class FlowContent(RequestHandler):
|
||||
|
||||
|
||||
class Events(RequestHandler):
|
||||
|
||||
def get(self):
|
||||
self.write(dict(
|
||||
data=list(self.state.events)
|
||||
@ -214,6 +227,7 @@ class Events(RequestHandler):
|
||||
|
||||
|
||||
class Settings(RequestHandler):
|
||||
|
||||
def get(self):
|
||||
self.write(dict(
|
||||
data=dict(
|
||||
@ -240,6 +254,7 @@ class Settings(RequestHandler):
|
||||
|
||||
|
||||
class Application(tornado.web.Application):
|
||||
|
||||
def __init__(self, master, debug):
|
||||
self.master = master
|
||||
handlers = [
|
||||
|
@ -1,4 +1,4 @@
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--var', type=int)
|
||||
parser.add_argument('--var', type=int)
|
||||
|
@ -1,3 +1,3 @@
|
||||
def tcp_message(ctx,tm):
|
||||
def tcp_message(ctx, tm):
|
||||
if tm.sender == tm.server_conn:
|
||||
tm.message = tm.message.replace("foo", "bar")
|
||||
|
@ -1,2 +1,2 @@
|
||||
def done(ctx):
|
||||
raise RuntimeError()
|
||||
raise RuntimeError()
|
||||
|
@ -3,6 +3,7 @@ import tservers
|
||||
|
||||
|
||||
class TestApp(tservers.HTTPProxTest):
|
||||
|
||||
def test_basic(self):
|
||||
assert self.app("/").status_code == 200
|
||||
|
||||
|
@ -8,6 +8,7 @@ import tutils
|
||||
|
||||
|
||||
class TestConsoleState:
|
||||
|
||||
def test_flow(self):
|
||||
"""
|
||||
normal flow:
|
||||
|
@ -7,11 +7,13 @@ import libmproxy.console.help as help
|
||||
|
||||
|
||||
class DummyLoop:
|
||||
|
||||
def __init__(self):
|
||||
self.widget = None
|
||||
|
||||
|
||||
class DummyMaster:
|
||||
|
||||
def __init__(self):
|
||||
self.loop = DummyLoop()
|
||||
|
||||
@ -20,6 +22,7 @@ class DummyMaster:
|
||||
|
||||
|
||||
class TestHelp:
|
||||
|
||||
def test_helptext(self):
|
||||
h = help.HelpView(None)
|
||||
assert h.helptext()
|
||||
|
@ -6,6 +6,7 @@ import libmproxy.console.palettes as palettes
|
||||
|
||||
|
||||
class TestPalette:
|
||||
|
||||
def test_helptext(self):
|
||||
for i in palettes.palettes.values():
|
||||
assert i.palette(False)
|
||||
|
@ -6,6 +6,7 @@ import tutils
|
||||
|
||||
|
||||
class TestPathCompleter:
|
||||
|
||||
def test_lookup_construction(self):
|
||||
c = pathedit._PathCompleter()
|
||||
|
||||
|
@ -3,6 +3,7 @@ from libmproxy import controller
|
||||
|
||||
|
||||
class TestMaster:
|
||||
|
||||
def test_default_handler(self):
|
||||
m = controller.Master(None)
|
||||
msg = mock.MagicMock()
|
||||
|
@ -11,7 +11,6 @@ def test_custom_views():
|
||||
def __call__(self, data, **metadata):
|
||||
return "noop", cv.format_text(data)
|
||||
|
||||
|
||||
view_obj = ViewNoop()
|
||||
|
||||
cv.add(view_obj)
|
||||
@ -47,5 +46,3 @@ def test_custom_views():
|
||||
)
|
||||
)
|
||||
assert "noop" not in r[0]
|
||||
|
||||
|
||||
|
@ -43,7 +43,6 @@ def test_strfuncs():
|
||||
flow.response.status_code = 300
|
||||
m.echo_flow(flow)
|
||||
|
||||
|
||||
flow = tutils.tflow(resp=netlib.tutils.tresp(content="{"))
|
||||
flow.response.headers["content-type"] = "application/json"
|
||||
flow.response.status_code = 400
|
||||
@ -61,6 +60,7 @@ def test_contentview(get_content_view):
|
||||
|
||||
|
||||
class TestDumpMaster:
|
||||
|
||||
def _cycle(self, m, content):
|
||||
f = tutils.tflow(req=netlib.tutils.treq(content=content))
|
||||
l = Log("connect")
|
||||
|
@ -7,6 +7,7 @@ import tutils
|
||||
|
||||
|
||||
class TestParsing:
|
||||
|
||||
def _dump(self, x):
|
||||
c = cStringIO.StringIO()
|
||||
x.dump(fp=c)
|
||||
@ -75,6 +76,7 @@ class TestParsing:
|
||||
|
||||
|
||||
class TestMatching:
|
||||
|
||||
def req(self):
|
||||
headers = Headers(header="qvalue")
|
||||
req = http.HTTPRequest(
|
||||
|
@ -43,6 +43,7 @@ def test_app_registry():
|
||||
|
||||
|
||||
class TestStickyCookieState:
|
||||
|
||||
def _response(self, cookie, host):
|
||||
s = flow.StickyCookieState(filt.parse(".*"))
|
||||
f = tutils.tflow(req=netlib.tutils.treq(host=host, port=80), resp=True)
|
||||
@ -76,6 +77,7 @@ class TestStickyCookieState:
|
||||
|
||||
|
||||
class TestStickyAuthState:
|
||||
|
||||
def test_handle_response(self):
|
||||
s = flow.StickyAuthState(filt.parse(".*"))
|
||||
f = tutils.tflow(resp=True)
|
||||
@ -89,6 +91,7 @@ class TestStickyAuthState:
|
||||
|
||||
|
||||
class TestClientPlaybackState:
|
||||
|
||||
def test_tick(self):
|
||||
first = tutils.tflow()
|
||||
s = flow.State()
|
||||
@ -122,6 +125,7 @@ class TestClientPlaybackState:
|
||||
|
||||
|
||||
class TestServerPlaybackState:
|
||||
|
||||
def test_hash(self):
|
||||
s = flow.ServerPlaybackState(
|
||||
None,
|
||||
@ -343,6 +347,7 @@ class TestServerPlaybackState:
|
||||
|
||||
|
||||
class TestFlow(object):
|
||||
|
||||
def test_copy(self):
|
||||
f = tutils.tflow(resp=True)
|
||||
a0 = f.get_state()
|
||||
@ -492,6 +497,7 @@ class TestFlow(object):
|
||||
|
||||
|
||||
class TestState:
|
||||
|
||||
def test_backup(self):
|
||||
c = flow.State()
|
||||
f = tutils.tflow()
|
||||
@ -629,6 +635,7 @@ class TestState:
|
||||
|
||||
|
||||
class TestSerialize:
|
||||
|
||||
def _treader(self):
|
||||
sio = StringIO()
|
||||
w = flow.FlowWriter(sio)
|
||||
@ -716,6 +723,7 @@ class TestSerialize:
|
||||
|
||||
|
||||
class TestFlowMaster:
|
||||
|
||||
def test_load_script(self):
|
||||
s = flow.State()
|
||||
fm = flow.FlowMaster(None, s)
|
||||
@ -995,6 +1003,7 @@ class TestFlowMaster:
|
||||
|
||||
|
||||
class TestRequest:
|
||||
|
||||
def test_simple(self):
|
||||
f = tutils.tflow()
|
||||
r = f.request
|
||||
@ -1113,7 +1122,7 @@ class TestRequest:
|
||||
r = HTTPRequest.wrap(netlib.tutils.treq())
|
||||
r.content = None
|
||||
r.headers["content-encoding"] = "identity"
|
||||
assert r.get_decoded_content() == None
|
||||
assert r.get_decoded_content() is None
|
||||
|
||||
r.content = "falafel"
|
||||
r.encode("gzip")
|
||||
@ -1126,6 +1135,7 @@ class TestRequest:
|
||||
|
||||
|
||||
class TestResponse:
|
||||
|
||||
def test_simple(self):
|
||||
f = tutils.tflow(resp=True)
|
||||
resp = f.response
|
||||
@ -1179,6 +1189,7 @@ class TestResponse:
|
||||
|
||||
|
||||
class TestError:
|
||||
|
||||
def test_getset_state(self):
|
||||
e = Error("Error")
|
||||
state = e.get_state()
|
||||
@ -1196,6 +1207,7 @@ class TestError:
|
||||
|
||||
|
||||
class TestClientConnection:
|
||||
|
||||
def test_state(self):
|
||||
|
||||
c = tutils.tclient_conn()
|
||||
|
@ -14,4 +14,4 @@ def test_cannot_convert():
|
||||
with open(tutils.test_data.path("data/dumpfile-012"), "rb") as f:
|
||||
flow_reader = FlowReader(f)
|
||||
with tutils.raises(FlowReadError):
|
||||
list(flow_reader.stream())
|
||||
list(flow_reader.stream())
|
||||
|
@ -7,6 +7,7 @@ import tservers
|
||||
|
||||
|
||||
class TestFuzzy(tservers.HTTPProxTest):
|
||||
|
||||
def test_idna_err(self):
|
||||
req = r'get:"http://localhost:%s":i10,"\xc6"'
|
||||
p = self.pathoc()
|
||||
|
@ -4,6 +4,7 @@ from libmproxy.platform import pf
|
||||
|
||||
|
||||
class TestLookup:
|
||||
|
||||
def test_simple(self):
|
||||
if sys.platform == "freebsd10":
|
||||
p = tutils.test_data.path("data/pf02")
|
||||
|
@ -8,6 +8,7 @@ import tservers
|
||||
|
||||
|
||||
class TestHTTPResponse:
|
||||
|
||||
def test_read_from_stringio(self):
|
||||
s = (
|
||||
b"HTTP/1.1 200 OK\r\n"
|
||||
@ -34,6 +35,7 @@ class TestHTTPResponse:
|
||||
|
||||
|
||||
class TestHTTPFlow(object):
|
||||
|
||||
def test_repr(self):
|
||||
f = tutils.tflow(resp=True, err=True)
|
||||
assert repr(f)
|
||||
@ -57,6 +59,7 @@ class TestInvalidRequests(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class TestExpectHeader(tservers.HTTPProxTest):
|
||||
|
||||
def test_simple(self):
|
||||
client = TCPClient(("127.0.0.1", self.proxy.port))
|
||||
client.connect()
|
||||
@ -83,7 +86,8 @@ class TestExpectHeader(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class TestHeadContentLength(tservers.HTTPProxTest):
|
||||
|
||||
def test_head_content_length(self):
|
||||
p = self.pathoc()
|
||||
resp = p.request("""head:'%s/p/200:h"Content-Length"="42"'""" % self.server.urlbase)
|
||||
assert resp.headers["Content-Length"] == "42"
|
||||
assert resp.headers["Content-Length"] == "42"
|
||||
|
@ -54,6 +54,7 @@ class TestServerConnection(object):
|
||||
|
||||
|
||||
class TestProcessProxyOptions:
|
||||
|
||||
def p(self, *args):
|
||||
parser = tutils.MockParser()
|
||||
cmdline.common_options(parser)
|
||||
@ -151,6 +152,7 @@ class TestProcessProxyOptions:
|
||||
|
||||
class TestProxyServer:
|
||||
# binding to 0.0.0.0:1 works without special permissions on Windows
|
||||
|
||||
@tutils.skip_windows
|
||||
def test_err(self):
|
||||
conf = ProxyConfig(
|
||||
@ -166,6 +168,7 @@ class TestProxyServer:
|
||||
|
||||
|
||||
class TestDummyServer:
|
||||
|
||||
def test_simple(self):
|
||||
d = DummyServer(None)
|
||||
d.start_slave()
|
||||
@ -173,6 +176,7 @@ class TestDummyServer:
|
||||
|
||||
|
||||
class TestConnectionHandler:
|
||||
|
||||
def test_fatal_error(self):
|
||||
config = mock.Mock()
|
||||
root_layer = mock.Mock()
|
||||
|
@ -91,6 +91,7 @@ def test_concurrent2():
|
||||
m = mock.Mock()
|
||||
|
||||
class Dummy:
|
||||
|
||||
def __init__(self):
|
||||
self.response = self
|
||||
self.error = self
|
||||
@ -129,4 +130,3 @@ def test_command_parsing():
|
||||
absfilepath = os.path.normcase(tutils.test_data.path("scripts/a.py"))
|
||||
s = script.Script(absfilepath, script.ScriptContext(fm))
|
||||
assert os.path.isfile(s.args[0])
|
||||
|
||||
|
@ -29,6 +29,7 @@ import tservers
|
||||
|
||||
|
||||
class CommonMixin:
|
||||
|
||||
def test_large(self):
|
||||
assert len(self.pathod("200:b@50k").content) == 1024 * 50
|
||||
|
||||
@ -107,6 +108,7 @@ class CommonMixin:
|
||||
|
||||
|
||||
class TcpMixin:
|
||||
|
||||
def _ignore_on(self):
|
||||
assert not hasattr(self, "_ignore_backup")
|
||||
self._ignore_backup = self.config.check_ignore
|
||||
@ -194,6 +196,7 @@ class TcpMixin:
|
||||
|
||||
|
||||
class AppMixin:
|
||||
|
||||
def test_app(self):
|
||||
ret = self.app("/")
|
||||
assert ret.status_code == 200
|
||||
@ -201,6 +204,7 @@ class AppMixin:
|
||||
|
||||
|
||||
class TestHTTP(tservers.HTTPProxTest, CommonMixin, AppMixin):
|
||||
|
||||
def test_app_err(self):
|
||||
p = self.pathoc()
|
||||
ret = p.request("get:'http://errapp/'")
|
||||
@ -348,6 +352,7 @@ class TestHTTPSCertfile(tservers.HTTPProxTest, CommonMixin):
|
||||
|
||||
|
||||
class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxTest):
|
||||
|
||||
"""
|
||||
Test upstream server certificate verification with a trusted server cert.
|
||||
"""
|
||||
@ -374,6 +379,7 @@ class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxTest):
|
||||
|
||||
"""
|
||||
Test upstream server certificate verification with an untrusted server cert.
|
||||
"""
|
||||
@ -412,6 +418,7 @@ class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class TestHTTPSNoCommonName(tservers.HTTPProxTest):
|
||||
|
||||
"""
|
||||
Test what happens if we get a cert without common name back.
|
||||
"""
|
||||
@ -432,6 +439,7 @@ class TestReverse(tservers.ReverseProxTest, CommonMixin, TcpMixin):
|
||||
|
||||
|
||||
class TestSocks5(tservers.SocksModeTest):
|
||||
|
||||
def test_simple(self):
|
||||
p = self.pathoc()
|
||||
p.socks_connect(("localhost", self.server.port))
|
||||
@ -469,6 +477,7 @@ class TestSocks5(tservers.SocksModeTest):
|
||||
|
||||
|
||||
class TestHttps2Http(tservers.ReverseProxTest):
|
||||
|
||||
@classmethod
|
||||
def get_proxy_config(cls):
|
||||
d = super(TestHttps2Http, cls).get_proxy_config()
|
||||
@ -526,6 +535,7 @@ class TestTransparentSSL(tservers.TransparentProxTest, CommonMixin, TcpMixin):
|
||||
|
||||
|
||||
class TestProxy(tservers.HTTPProxTest):
|
||||
|
||||
def test_http(self):
|
||||
f = self.pathod("304")
|
||||
assert f.status_code == 304
|
||||
@ -562,7 +572,7 @@ class TestProxy(tservers.HTTPProxTest):
|
||||
connection.close()
|
||||
|
||||
request, response = self.master.state.view[
|
||||
0].request, self.master.state.view[0].response
|
||||
0].request, self.master.state.view[0].response
|
||||
assert response.status_code == 304 # sanity test for our low level request
|
||||
# timestamp_start might fire a bit late, so we play safe and only require 300ms.
|
||||
assert 0.3 <= request.timestamp_end - request.timestamp_start
|
||||
@ -678,6 +688,7 @@ class TestRedirectRequest(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class MasterStreamRequest(tservers.TestMaster):
|
||||
|
||||
"""
|
||||
Enables the stream flag on the flow for all requests
|
||||
"""
|
||||
@ -731,6 +742,7 @@ class TestStreamRequest(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class MasterFakeResponse(tservers.TestMaster):
|
||||
|
||||
def handle_request(self, f):
|
||||
resp = HTTPResponse.wrap(netlib.tutils.tresp())
|
||||
f.reply(resp)
|
||||
@ -748,6 +760,7 @@ class TestServerConnect(tservers.HTTPProxTest):
|
||||
masterclass = MasterFakeResponse
|
||||
no_upstream_cert = True
|
||||
ssl = True
|
||||
|
||||
def test_unnecessary_serverconnect(self):
|
||||
"""A replayed/fake response with no_upstream_cert should not connect to an upstream server"""
|
||||
assert self.pathod("200").status_code == 200
|
||||
@ -756,6 +769,7 @@ class TestServerConnect(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class MasterKillRequest(tservers.TestMaster):
|
||||
|
||||
def handle_request(self, f):
|
||||
f.reply(Kill)
|
||||
|
||||
@ -771,6 +785,7 @@ class TestKillRequest(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class MasterKillResponse(tservers.TestMaster):
|
||||
|
||||
def handle_response(self, f):
|
||||
f.reply(Kill)
|
||||
|
||||
@ -786,6 +801,7 @@ class TestKillResponse(tservers.HTTPProxTest):
|
||||
|
||||
|
||||
class EResolver(tservers.TResolver):
|
||||
|
||||
def original_addr(self, sock):
|
||||
raise RuntimeError("Could not resolve original destination.")
|
||||
|
||||
@ -798,6 +814,7 @@ class TestTransparentResolveError(tservers.TransparentProxTest):
|
||||
|
||||
|
||||
class MasterIncomplete(tservers.TestMaster):
|
||||
|
||||
def handle_request(self, f):
|
||||
resp = HTTPResponse.wrap(netlib.tutils.tresp())
|
||||
resp.content = CONTENT_MISSING
|
||||
@ -833,9 +850,9 @@ class TestUpstreamProxy(tservers.HTTPUpstreamProxTest, CommonMixin, AppMixin):
|
||||
|
||||
|
||||
class TestUpstreamProxySSL(
|
||||
tservers.HTTPUpstreamProxTest,
|
||||
CommonMixin,
|
||||
TcpMixin):
|
||||
tservers.HTTPUpstreamProxTest,
|
||||
CommonMixin,
|
||||
TcpMixin):
|
||||
ssl = True
|
||||
|
||||
def _host_pattern_on(self, attr):
|
||||
@ -930,7 +947,7 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxTest):
|
||||
exclude=[
|
||||
# fail first request
|
||||
2, # allow second request
|
||||
])
|
||||
])
|
||||
|
||||
kill_requests(self.chain[0].tmaster, "handle_request",
|
||||
exclude=[
|
||||
@ -938,7 +955,7 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxTest):
|
||||
# fail first request
|
||||
3, # reCONNECT
|
||||
4, # request
|
||||
])
|
||||
])
|
||||
|
||||
p = self.pathoc()
|
||||
req = p.request("get:'/p/418:b\"content\"'")
|
||||
@ -954,7 +971,6 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxTest):
|
||||
# (doesn't store (repeated) CONNECTs from chain[0]
|
||||
# as it is a regular proxy)
|
||||
|
||||
|
||||
assert not self.chain[1].tmaster.state.flows[0].response # killed
|
||||
assert self.chain[1].tmaster.state.flows[1].response
|
||||
|
||||
@ -962,18 +978,18 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxTest):
|
||||
assert self.proxy.tmaster.state.flows[1].request.form_in == "relative"
|
||||
|
||||
assert self.chain[0].tmaster.state.flows[
|
||||
0].request.form_in == "authority"
|
||||
0].request.form_in == "authority"
|
||||
assert self.chain[0].tmaster.state.flows[
|
||||
1].request.form_in == "relative"
|
||||
1].request.form_in == "relative"
|
||||
assert self.chain[0].tmaster.state.flows[
|
||||
2].request.form_in == "authority"
|
||||
2].request.form_in == "authority"
|
||||
assert self.chain[0].tmaster.state.flows[
|
||||
3].request.form_in == "relative"
|
||||
3].request.form_in == "relative"
|
||||
|
||||
assert self.chain[1].tmaster.state.flows[
|
||||
0].request.form_in == "relative"
|
||||
0].request.form_in == "relative"
|
||||
assert self.chain[1].tmaster.state.flows[
|
||||
1].request.form_in == "relative"
|
||||
1].request.form_in == "relative"
|
||||
|
||||
req = p.request("get:'/p/418:b\"content2\"'")
|
||||
|
||||
|
@ -15,6 +15,7 @@ import click
|
||||
|
||||
|
||||
class ApacheBenchThread(Thread):
|
||||
|
||||
def __init__(self, concurrency):
|
||||
self.concurrency = concurrency
|
||||
super(ApacheBenchThread, self).__init__()
|
||||
|
@ -15,6 +15,7 @@ def read_tnetstring(input):
|
||||
input.seek(-1, 1)
|
||||
return tnetstring.load(input)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("input", type=click.File('rb'))
|
||||
def inspect(input):
|
||||
|
@ -18,7 +18,7 @@ def str_fun(obj):
|
||||
return "(-locals-)"
|
||||
if "self" in obj and isinstance(obj["self"], refbrowser.InteractiveBrowser):
|
||||
return "(-browser-)"
|
||||
return str(id(obj)) + ": " + str(obj)[:100].replace("\r\n","\\r\\n").replace("\n","\\n")
|
||||
return str(id(obj)) + ": " + str(obj)[:100].replace("\r\n", "\\r\\n").replace("\n", "\\n")
|
||||
|
||||
|
||||
def request(ctx, flow):
|
||||
@ -35,4 +35,4 @@ def request(ctx, flow):
|
||||
ib = refbrowser.InteractiveBrowser(ssl, 2, str_fun, repeat=False)
|
||||
del ssl # do this to unpollute view
|
||||
ib.main(True)
|
||||
# print("\r\n".join(str(x)[:100] for x in gc.get_referrers(ssl)))
|
||||
# print("\r\n".join(str(x)[:100] for x in gc.get_referrers(ssl)))
|
||||
|
@ -3,6 +3,7 @@ from time import sleep
|
||||
|
||||
|
||||
class service(SocketServer.BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
data = 'dummy'
|
||||
print "Client connected with ", self.client_address
|
||||
|
@ -29,6 +29,7 @@ def errapp(environ, start_response):
|
||||
|
||||
|
||||
class TestMaster(flow.FlowMaster):
|
||||
|
||||
def __init__(self, config):
|
||||
config.port = 0
|
||||
s = ProxyServer(config)
|
||||
@ -55,6 +56,7 @@ class TestMaster(flow.FlowMaster):
|
||||
|
||||
|
||||
class ProxyThread(threading.Thread):
|
||||
|
||||
def __init__(self, tmaster):
|
||||
threading.Thread.__init__(self)
|
||||
self.tmaster = tmaster
|
||||
@ -131,6 +133,7 @@ class ProxTestBase(object):
|
||||
|
||||
|
||||
class HTTPProxTest(ProxTestBase):
|
||||
|
||||
def pathoc_raw(self):
|
||||
return libpathod.pathoc.Pathoc(("127.0.0.1", self.proxy.port), fp=None)
|
||||
|
||||
@ -172,12 +175,14 @@ class HTTPProxTest(ProxTestBase):
|
||||
|
||||
|
||||
class TResolver:
|
||||
|
||||
def __init__(self, port):
|
||||
self.port = port
|
||||
|
||||
def original_addr(self, sock):
|
||||
return ("127.0.0.1", self.port)
|
||||
|
||||
|
||||
class TransparentProxTest(ProxTestBase):
|
||||
ssl = None
|
||||
resolver = TResolver
|
||||
@ -263,6 +268,7 @@ class ReverseProxTest(ProxTestBase):
|
||||
|
||||
|
||||
class SocksModeTest(HTTPProxTest):
|
||||
|
||||
@classmethod
|
||||
def get_proxy_config(cls):
|
||||
d = ProxTestBase.get_proxy_config()
|
||||
@ -271,6 +277,7 @@ class SocksModeTest(HTTPProxTest):
|
||||
|
||||
|
||||
class ChainProxTest(ProxTestBase):
|
||||
|
||||
"""
|
||||
Chain three instances of mitmproxy in a row to test upstream mode.
|
||||
Proxy order is cls.proxy -> cls.chain[0] -> cls.chain[1]
|
||||
|
@ -121,6 +121,7 @@ def tmpdir(*args, **kwargs):
|
||||
|
||||
|
||||
class MockParser(argparse.ArgumentParser):
|
||||
|
||||
"""
|
||||
argparse.ArgumentParser sys.exits() by default.
|
||||
Make it more testable by throwing an exception instead.
|
||||
|
Loading…
Reference in New Issue
Block a user