diff options
Diffstat (limited to 'libmproxy')
54 files changed, 374 insertions, 134 deletions
diff --git a/libmproxy/cmdline.py b/libmproxy/cmdline.py index cd1a8bba..111ab145 100644 --- a/libmproxy/cmdline.py +++ b/libmproxy/cmdline.py @@ -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): diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py index 3d0fb1aa..80a6e28a 100644 --- a/libmproxy/console/__init__.py +++ b/libmproxy/console/__init__.py @@ -22,6 +22,7 @@ EVENTLOG_SIZE = 500 class ConsoleState(flow.State): + def __init__(self): flow.State.__init__(self) self.focus = None diff --git a/libmproxy/console/common.py b/libmproxy/console/common.py index 12fdfe27..276e45c2 100644 --- a/libmproxy/console/common.py +++ b/libmproxy/console/common.py @@ -207,7 +207,7 @@ def raw_format_flow(f, focus, extended, padding): # Save file to disk -def save_data(path, data, master, state): +def save_data(path, data): if not path: return try: @@ -217,32 +217,32 @@ def save_data(path, data, master, state): signals.status_message.send(message=v.strerror) -def ask_save_overwite(path, data, master, state): +def ask_save_overwrite(path, data): if not path: return path = os.path.expanduser(path) if os.path.exists(path): - def save_overwite(k): + def save_overwrite(k): if k == "y": - save_data(path, data, master, state) + save_data(path, data) signals.status_prompt_onekey.send( - prompt = "'" + path + "' already exists. Overwite?", + prompt = "'" + path + "' already exists. Overwrite?", keys = ( ("yes", "y"), ("no", "n"), ), - callback = save_overwite + callback = save_overwrite ) else: - save_data(path, data, master, state) + save_data(path, data) -def ask_save_path(prompt, data, master, state): +def ask_save_path(prompt, data): signals.status_prompt_path.send( prompt = prompt, - callback = ask_save_overwite, - args = (data, master, state) + callback = ask_save_overwrite, + args = (data, ) ) @@ -277,26 +277,60 @@ def copy_flow_format_data(part, scope, flow): return data, False -def copy_flow(part, scope, flow, master, state): - """ - part: _c_ontent, _h_eaders+content, _u_rl - scope: _a_ll, re_q_uest, re_s_ponse - """ - data, err = copy_flow_format_data(part, scope, flow) +def export_prompt(k, flow): + if k == "c": + copy_as_curl_command(flow) + elif k == "p": + copy_as_python_code(flow) + elif k == "r": + copy_as_raw_request(flow) - if err: - signals.status_message.send(message=err) - return - if not data: - if scope == "q": - signals.status_message.send(message="No request content to copy.") - elif scope == "s": - signals.status_message.send(message="No response content to copy.") - else: - signals.status_message.send(message="No contents to copy.") +def copy_as_curl_command(flow): + data = "curl " + + for k, v in flow.request.headers.fields: + data += "-H '%s:%s' " % (k, v) + + if flow.request.method != "GET": + 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 + + copy_to_clipboard_or_prompt(data) + + +def copy_as_python_code(flow): + if flow.request.method != "GET": + signals.status_message.send(message="Currently, only GET methods are supported") return + data = ("import requests\n" + "headers = {%s}\n" + "url = '%s'\n" + "resp = requests.get(url, headers=headers)") + + headers = "\n" + for k, v in flow.request.headers.fields: + headers += " '%s': '%s',\n" % (k, v) + + full_url = flow.request.scheme + "://" + flow.request.host + flow.request.path + + data = data % (headers, full_url) + + copy_to_clipboard_or_prompt(data) + + +def copy_as_raw_request(flow): + data = netlib.http.http1.assemble_request(flow.request) + copy_to_clipboard_or_prompt(data) + + +def copy_to_clipboard_or_prompt(data): # pyperclip calls encode('utf-8') on data to be copied without checking. # if data are already encoded that way UnicodeDecodeError is thrown. toclip = "" @@ -310,7 +344,7 @@ def copy_flow(part, scope, flow, master, state): except (RuntimeError, UnicodeDecodeError, AttributeError): def save(k): if k == "y": - ask_save_path("Save data", data, master, state) + ask_save_path("Save data", data) signals.status_prompt_onekey.send( prompt = "Cannot copy data to clipboard. Save as file?", keys = ( @@ -321,6 +355,29 @@ def copy_flow(part, scope, flow, master, state): ) +def copy_flow(part, scope, flow, master, state): + """ + part: _c_ontent, _h_eaders+content, _u_rl + scope: _a_ll, re_q_uest, re_s_ponse + """ + data, err = copy_flow_format_data(part, scope, flow) + + if err: + signals.status_message.send(message=err) + return + + if not data: + if scope == "q": + signals.status_message.send(message="No request content to copy.") + elif scope == "s": + signals.status_message.send(message="No response content to copy.") + else: + signals.status_message.send(message="No contents to copy.") + return + + copy_to_clipboard_or_prompt(data) + + def ask_copy_part(scope, flow, master, state): choices = [ ("content", "c"), @@ -367,16 +424,12 @@ def ask_save_body(part, master, state, flow): elif part == "q" and request_has_content: ask_save_path( "Save request content", - flow.request.get_decoded_content(), - master, - state + flow.request.get_decoded_content() ) elif part == "s" and response_has_content: ask_save_path( "Save response content", - flow.response.get_decoded_content(), - master, - state + flow.response.get_decoded_content() ) else: signals.status_message.send(message="No content to save.") @@ -386,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, diff --git a/libmproxy/console/flowdetailview.py b/libmproxy/console/flowdetailview.py index 40769c95..f4b4262e 100644 --- a/libmproxy/console/flowdetailview.py +++ b/libmproxy/console/flowdetailview.py @@ -9,7 +9,6 @@ def maybe_timestamp(base, attr): return utils.format_timestamp_with_milli(getattr(base, attr)) else: return "active" - pass def flowdetails(state, flow): @@ -20,7 +19,7 @@ def flowdetails(state, flow): req = flow.request resp = flow.response - if sc: + if sc is not None: text.append(urwid.Text([("head", "Server Connection:")])) parts = [ ["Address", "%s:%s" % sc.address()], @@ -76,7 +75,7 @@ def flowdetails(state, flow): common.format_keyvals(parts, key="key", val="text", indent=4) ) - if cc: + if cc is not None: text.append(urwid.Text([("head", "Client Connection:")])) parts = [ diff --git a/libmproxy/console/flowlist.py b/libmproxy/console/flowlist.py index 2b77f4a3..c2201055 100644 --- a/libmproxy/console/flowlist.py +++ b/libmproxy/console/flowlist.py @@ -1,7 +1,6 @@ from __future__ import absolute_import import urwid -from netlib import http import netlib.utils from . import common, signals @@ -16,6 +15,7 @@ def _mkhelp(): ("C", "clear flow list or eventlog"), ("d", "delete flow"), ("D", "duplicate flow"), + ("E", "export"), ("e", "toggle eventlog"), ("F", "toggle follow flow list"), ("l", "set limit filter pattern"), @@ -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 @@ -254,6 +257,18 @@ class ConnectionItem(urwid.WidgetWrap): ) elif key == "P": common.ask_copy_part("a", self.flow, self.master, self.state) + elif key == "E": + signals.status_prompt_onekey.send( + self, + prompt = "Export", + keys = ( + ("as curl command", "c"), + ("as python code", "p"), + ("as raw request", "r"), + ), + callback = common.export_prompt, + args = (self.flow,) + ) elif key == "b": common.ask_save_body(None, self.master, self.state, self.flow) else: @@ -261,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) @@ -289,6 +305,7 @@ class FlowListWalker(urwid.ListWalker): class FlowListBox(urwid.ListBox): + def __init__(self, master): self.master = master urwid.ListBox.__init__( diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py index 0038558b..8102de55 100644 --- a/libmproxy/console/flowview.py +++ b/libmproxy/console/flowview.py @@ -25,8 +25,9 @@ def _mkhelp(): ("A", "accept all intercepted flows"), ("a", "accept this intercepted flow"), ("b", "save request/response body"), - ("d", "delete flow"), ("D", "duplicate flow"), + ("d", "delete flow"), + ("E", "export"), ("e", "edit request/response"), ("f", "load full body data"), ("m", "change body display mode for this entity"), @@ -95,6 +96,7 @@ footer = [ class FlowViewHeader(urwid.WidgetWrap): + def __init__(self, master, f): self.master, self.flow = master, f self._w = common.format_flow( @@ -217,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: @@ -574,6 +576,18 @@ class FlowView(tabs.Tabs): callback = self.master.save_one_flow, args = (self.flow,) ) + elif key == "E": + signals.status_prompt_onekey.send( + self, + prompt = "Export", + keys = ( + ("as curl command", "c"), + ("as python code", "p"), + ("as raw request", "r"), + ), + callback = common.export_prompt, + args = (self.flow,) + ) elif key == "|": signals.status_prompt_path.send( prompt = "Send flow to script", diff --git a/libmproxy/console/grideditor.py b/libmproxy/console/grideditor.py index 237eea28..a11c962c 100644 --- a/libmproxy/console/grideditor.py +++ b/libmproxy/console/grideditor.py @@ -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) diff --git a/libmproxy/console/help.py b/libmproxy/console/help.py index 74748030..0c264ebf 100644 --- a/libmproxy/console/help.py +++ b/libmproxy/console/help.py @@ -12,6 +12,7 @@ footer = [ class HelpView(urwid.ListBox): + def __init__(self, help_context): self.help_context = help_context or [] urwid.ListBox.__init__( diff --git a/libmproxy/console/options.py b/libmproxy/console/options.py index a365a78c..5c9e0cc9 100644 --- a/libmproxy/console/options.py +++ b/libmproxy/console/options.py @@ -22,6 +22,7 @@ help_context = _mkhelp() class Options(urwid.WidgetWrap): + def __init__(self, master): self.master = master self.lb = select.Select( diff --git a/libmproxy/console/palettepicker.py b/libmproxy/console/palettepicker.py index 7e2c10cd..51ad0606 100644 --- a/libmproxy/console/palettepicker.py +++ b/libmproxy/console/palettepicker.py @@ -18,6 +18,7 @@ help_context = _mkhelp() class PalettePicker(urwid.WidgetWrap): + def __init__(self, master): self.master = master low, high = [], [] diff --git a/libmproxy/console/palettes.py b/libmproxy/console/palettes.py index d897a0a2..bd370181 100644 --- a/libmproxy/console/palettes.py +++ b/libmproxy/console/palettes.py @@ -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 """ diff --git a/libmproxy/console/pathedit.py b/libmproxy/console/pathedit.py index dccec14a..4447070b 100644 --- a/libmproxy/console/pathedit.py +++ b/libmproxy/console/pathedit.py @@ -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) diff --git a/libmproxy/console/searchable.py b/libmproxy/console/searchable.py index dea0ac7f..cff1f0a1 100644 --- a/libmproxy/console/searchable.py +++ b/libmproxy/console/searchable.py @@ -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) diff --git a/libmproxy/console/select.py b/libmproxy/console/select.py index bf96a785..928a7ca5 100644 --- a/libmproxy/console/select.py +++ b/libmproxy/console/select.py @@ -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__( diff --git a/libmproxy/console/signals.py b/libmproxy/console/signals.py index 52f72d34..6a439bf3 100644 --- a/libmproxy/console/signals.py +++ b/libmproxy/console/signals.py @@ -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, diff --git a/libmproxy/console/statusbar.py b/libmproxy/console/statusbar.py index fd084f8c..4cc63a54 100644 --- a/libmproxy/console/statusbar.py +++ b/libmproxy/console/statusbar.py @@ -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() diff --git a/libmproxy/console/tabs.py b/libmproxy/console/tabs.py index fea9bbde..b5423038 100644 --- a/libmproxy/console/tabs.py +++ b/libmproxy/console/tabs.py @@ -1,8 +1,8 @@ import urwid -import signals class Tab(urwid.WidgetWrap): + def __init__(self, offset, content, attr, onclick): """ onclick is called on click with the tab offset as argument @@ -21,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 diff --git a/libmproxy/console/window.py b/libmproxy/console/window.py index 69d5e242..47c284e4 100644 --- a/libmproxy/console/window.py +++ b/libmproxy/console/window.py @@ -3,6 +3,7 @@ from . import signals class Window(urwid.Frame): + def __init__(self, master, body, header, footer, helpctx): urwid.Frame.__init__( self, diff --git a/libmproxy/contentviews.py b/libmproxy/contentviews.py index eaab8169..526f5ff4 100644 --- a/libmproxy/contentviews.py +++ b/libmproxy/contentviews.py @@ -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 """ diff --git a/libmproxy/contrib/jsbeautifier/unpackers/__init__.py b/libmproxy/contrib/jsbeautifier/unpackers/__init__.py index 96318a5a..fcb5b07a 100644 --- a/libmproxy/contrib/jsbeautifier/unpackers/__init__.py +++ b/libmproxy/contrib/jsbeautifier/unpackers/__init__.py @@ -15,7 +15,6 @@ BLACKLIST = ['jsbeautifier.unpackers.evalbased'] class UnpackingError(Exception): """Badly packed source or general error. Argument is a meaningful description.""" - pass def getunpackers(): """Scans the unpackers dir, finds unpackers and add them to UNPACKERS list. diff --git a/libmproxy/contrib/jsbeautifier/unpackers/packer.py b/libmproxy/contrib/jsbeautifier/unpackers/packer.py index 37aa7123..4ada669e 100644 --- a/libmproxy/contrib/jsbeautifier/unpackers/packer.py +++ b/libmproxy/contrib/jsbeautifier/unpackers/packer.py @@ -13,7 +13,6 @@ """Unpacker for Dean Edward's p.a.c.k.e.r""" import re -import string from . import UnpackingError PRIORITY = 1 diff --git a/libmproxy/controller.py b/libmproxy/controller.py index 24b229c5..712ab1d2 100644 --- a/libmproxy/controller.py +++ b/libmproxy/controller.py @@ -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. """ diff --git a/libmproxy/dump.py b/libmproxy/dump.py index c2a3268a..65e212bf 100644 --- a/libmproxy/dump.py +++ b/libmproxy/dump.py @@ -1,6 +1,4 @@ from __future__ import absolute_import, print_function -import sys -import os import traceback import click @@ -10,7 +8,7 @@ from netlib.http import CONTENT_MISSING import netlib.utils from . import flow, filt, contentviews from .exceptions import ContentViewException -from .models import HTTPRequest + class DumpError(Exception): pass @@ -58,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 @@ -171,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: @@ -237,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") diff --git a/libmproxy/exceptions.py b/libmproxy/exceptions.py index e2bde980..a5d35263 100644 --- a/libmproxy/exceptions.py +++ b/libmproxy/exceptions.py @@ -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
\ No newline at end of file + pass diff --git a/libmproxy/filt.py b/libmproxy/filt.py index acbf4ef9..aa62b717 100644 --- a/libmproxy/filt.py +++ b/libmproxy/filt.py @@ -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] diff --git a/libmproxy/flow.py b/libmproxy/flow.py index 1f28166f..9fe21810 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -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 @@ -652,7 +664,6 @@ class FlowMaster(controller.Master): """ level: debug, info, error """ - pass def unload_scripts(self): for s in self.scripts[:]: @@ -1100,6 +1111,7 @@ def read_flows_from_paths(paths): class FlowWriter: + def __init__(self, fo): self.fo = fo @@ -1109,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 @@ -1140,6 +1154,7 @@ class FlowReader: class FilteredFlowWriter: + def __init__(self, fo, filt): self.fo = fo self.filt = filt diff --git a/libmproxy/models/connections.py b/libmproxy/models/connections.py index f5dabe4e..a45e1629 100644 --- a/libmproxy/models/connections.py +++ b/libmproxy/models/connections.py @@ -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 diff --git a/libmproxy/models/flow.py b/libmproxy/models/flow.py index 8eff18f4..b4e8cb88 100644 --- a/libmproxy/models/flow.py +++ b/libmproxy/models/flow.py @@ -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. diff --git a/libmproxy/models/http.py b/libmproxy/models/http.py index dfa3a824..e07dff69 100644 --- a/libmproxy/models/http.py +++ b/libmproxy/models/http.py @@ -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"")
\ No newline at end of file +expect_continue_response = HTTPResponse(b"HTTP/1.1", 100, "Continue", Headers(), b"") diff --git a/libmproxy/onboarding/app.py b/libmproxy/onboarding/app.py index 6edd74b1..ff5ed63c 100644 --- a/libmproxy/onboarding/app.py +++ b/libmproxy/onboarding/app.py @@ -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" diff --git a/libmproxy/platform/linux.py b/libmproxy/platform/linux.py index e60a9950..38bfbe42 100644 --- a/libmproxy/platform/linux.py +++ b/libmproxy/platform/linux.py @@ -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) diff --git a/libmproxy/platform/osx.py b/libmproxy/platform/osx.py index 2824718e..afbc919b 100644 --- a/libmproxy/platform/osx.py +++ b/libmproxy/platform/osx.py @@ -21,7 +21,7 @@ class Resolver(object): peer = csock.getpeername() try: stxt = subprocess.check_output(self.STATECMD, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError, e: + except subprocess.CalledProcessError as e: if "sudo: a password is required" in e.output: insufficient_priv = True else: diff --git a/libmproxy/platform/windows.py b/libmproxy/platform/windows.py index 09a4422f..9fe04cfa 100644 --- a/libmproxy/platform/windows.py +++ b/libmproxy/platform/windows.py @@ -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. diff --git a/libmproxy/protocol/__init__.py b/libmproxy/protocol/__init__.py index 0d624fd7..d46f16f5 100644 --- a/libmproxy/protocol/__init__.py +++ b/libmproxy/protocol/__init__.py @@ -28,12 +28,14 @@ as late as possible; this makes server replay without any outgoing connections p from __future__ import (absolute_import, print_function, division) from .base import Layer, ServerConnectionMixin, Kill from .http import Http1Layer, UpstreamConnectLayer, Http2Layer -from .tls import TlsLayer, is_tls_record_magic +from .tls import TlsLayer +from .tls import is_tls_record_magic +from .tls import TlsClientHello from .rawtcp import RawTCPLayer __all__ = [ "Layer", "ServerConnectionMixin", "Kill", "Http1Layer", "UpstreamConnectLayer", "Http2Layer", - "TlsLayer", "is_tls_record_magic", - "RawTCPLayer" + "TlsLayer", "is_tls_record_magic", "TlsClientHello", + "RawTCPLayer", ] diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py index d984cadb..4eb034c0 100644 --- a/libmproxy/protocol/base.py +++ b/libmproxy/protocol/base.py @@ -3,13 +3,13 @@ import sys import six -from netlib import tcp from ..models import ServerConnection from ..exceptions import ProtocolException from netlib.exceptions import TcpException class _LayerCodeCompletion(object): + """ Dummy class that provides type hinting in PyCharm, which simplifies development a lot. """ @@ -31,6 +31,7 @@ class _LayerCodeCompletion(object): class Layer(_LayerCodeCompletion): + """ Base class for all layers. All other protocol layers should inherit from this class. """ @@ -91,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`. @@ -190,6 +192,7 @@ class ServerConnectionMixin(object): class Kill(Exception): + """ Signal that both client and server connection(s) should be killed immediately. """ diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py index d72adc37..12d09e71 100644 --- a/libmproxy/protocol/http.py +++ b/libmproxy/protocol/http.py @@ -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 diff --git a/libmproxy/protocol/rawtcp.py b/libmproxy/protocol/rawtcp.py index ccd3c7ec..b87899e4 100644 --- a/libmproxy/protocol/rawtcp.py +++ b/libmproxy/protocol/rawtcp.py @@ -1,6 +1,5 @@ from __future__ import (absolute_import, print_function, division) import socket -import select import six import sys @@ -14,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 diff --git a/libmproxy/protocol/tls.py b/libmproxy/protocol/tls.py index ed747643..28f8c177 100644 --- a/libmproxy/protocol/tls.py +++ b/libmproxy/protocol/tls.py @@ -5,14 +5,14 @@ import sys from construct import ConstructError import six -from netlib.exceptions import InvalidCertificateException, TcpException, TlsException +from netlib.exceptions import InvalidCertificateException +from netlib.exceptions import TlsException from ..contrib.tls._constructs import ClientHello from ..exceptions import ProtocolException, TlsProtocolException, ClientHandshakeException from .base import Layer - # taken from https://testssl.sh/openssl-rfc.mappping.html CIPHER_ID_NAME_MAP = { 0x00: 'NULL-MD5', @@ -222,7 +222,85 @@ def is_tls_record_magic(d): ) +def get_client_hello(client_conn): + """ + Peek into the socket and read all records that contain the initial client hello message. + + client_conn: + The :py:class:`client connection <libmproxy.models.ClientConnection>`. + + Returns: + The raw handshake packet bytes, without TLS record header(s). + """ + client_hello = "" + client_hello_size = 1 + offset = 0 + while len(client_hello) < client_hello_size: + record_header = client_conn.rfile.peek(offset + 5)[offset:] + if not is_tls_record_magic(record_header) or len(record_header) != 5: + raise TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header) + record_size = struct.unpack("!H", record_header[3:])[0] + 5 + record_body = client_conn.rfile.peek(offset + record_size)[offset + 5:] + if len(record_body) != record_size - 5: + raise TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body) + client_hello += record_body + offset += record_size + 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) + + def raw(self): + return self._client_hello + + @property + def client_cipher_suites(self): + return self._client_hello.cipher_suites.cipher_suites + + @property + def client_sni(self): + for extension in self._client_hello.extensions: + if (extension.type == 0x00 and len(extension.server_names) == 1 + and extension.server_names[0].type == 0): + return extension.server_names[0].name + + @property + def client_alpn_protocols(self): + for extension in self._client_hello.extensions: + if extension.type == 0x10: + return list(extension.alpn_protocols) + + @classmethod + def from_client_conn(cls, client_conn): + """ + Peek into the connection, read the initial client hello and parse it to obtain ALPN values. + client_conn: + The :py:class:`client connection <libmproxy.models.ClientConnection>`. + Returns: + :py:class:`client hello <libmproxy.protocol.tls.TlsClientHello>`. + """ + try: + raw_client_hello = get_client_hello(client_conn)[4:] # exclude handshake header. + except ProtocolException as e: + raise TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e)) + + 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"))) + + def __repr__(self): + return "TlsClientHello( sni: %s alpn_protocols: %s, cipher_suites: %s)" % \ + (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 @@ -281,60 +359,17 @@ class TlsLayer(Layer): else: return "TlsLayer(inactive)" - def _get_client_hello(self): - """ - Peek into the socket and read all records that contain the initial client hello message. - - Returns: - The raw handshake packet bytes, without TLS record header(s). - """ - client_hello = "" - client_hello_size = 1 - offset = 0 - while len(client_hello) < client_hello_size: - record_header = self.client_conn.rfile.peek(offset + 5)[offset:] - if not is_tls_record_magic(record_header) or len(record_header) != 5: - raise TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header) - record_size = struct.unpack("!H", record_header[3:])[0] + 5 - record_body = self.client_conn.rfile.peek(offset + record_size)[offset + 5:] - if len(record_body) != record_size - 5: - raise TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body) - client_hello += record_body - offset += record_size - client_hello_size = struct.unpack("!I", '\x00' + client_hello[1:4])[0] + 4 - return client_hello - def _parse_client_hello(self): """ Peek into the connection, read the initial client hello and parse it to obtain ALPN values. """ try: - raw_client_hello = self._get_client_hello()[4:] # exclude handshake header. - except ProtocolException as e: + 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 + except TlsProtocolException as e: self.log("Cannot parse Client Hello: %s" % repr(e), "error") - return - - try: - client_hello = ClientHello.parse(raw_client_hello) - except ConstructError as e: - self.log("Cannot parse Client Hello: %s" % repr(e), "error") - self.log("Raw Client Hello: %s" % raw_client_hello.encode("hex"), "debug") - return - - self.client_ciphers = client_hello.cipher_suites.cipher_suites - - for extension in client_hello.extensions: - if extension.type == 0x00: - if len(extension.server_names) != 1 or extension.server_names[0].type != 0: - self.log("Unknown Server Name Indication: %s" % extension.server_names, "error") - self.client_sni = extension.server_names[0].name - elif extension.type == 0x10: - self.client_alpn_protocols = list(extension.alpn_protocols) - - self.log( - "Parsed Client Hello: sni=%s, alpn=%s" % (self.client_sni, self.client_alpn_protocols), - "debug" - ) def connect(self): if not self.server_conn: @@ -435,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 diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py index b1478655..bf765d81 100644 --- a/libmproxy/proxy/config.py +++ b/libmproxy/proxy/config.py @@ -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='', diff --git a/libmproxy/proxy/modes/http_proxy.py b/libmproxy/proxy/modes/http_proxy.py index c7502c24..e19062b9 100644 --- a/libmproxy/proxy/modes/http_proxy.py +++ b/libmproxy/proxy/modes/http_proxy.py @@ -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) diff --git a/libmproxy/proxy/modes/reverse_proxy.py b/libmproxy/proxy/modes/reverse_proxy.py index 28f4e6f8..c8e80a10 100644 --- a/libmproxy/proxy/modes/reverse_proxy.py +++ b/libmproxy/proxy/modes/reverse_proxy.py @@ -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 diff --git a/libmproxy/proxy/modes/socks_proxy.py b/libmproxy/proxy/modes/socks_proxy.py index 90788e37..e2ce44ae 100644 --- a/libmproxy/proxy/modes/socks_proxy.py +++ b/libmproxy/proxy/modes/socks_proxy.py @@ -8,6 +8,7 @@ from ...protocol import Layer, ServerConnectionMixin class Socks5Proxy(Layer, ServerConnectionMixin): + def __init__(self, ctx): super(Socks5Proxy, self).__init__(ctx) diff --git a/libmproxy/proxy/modes/transparent_proxy.py b/libmproxy/proxy/modes/transparent_proxy.py index da1d4632..3fdda656 100644 --- a/libmproxy/proxy/modes/transparent_proxy.py +++ b/libmproxy/proxy/modes/transparent_proxy.py @@ -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() diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py index f62b0c8e..f56aee6d 100644 --- a/libmproxy/proxy/root_context.py +++ b/libmproxy/proxy/root_context.py @@ -4,16 +4,17 @@ import sys import six -from libmproxy.exceptions import ProtocolException +from libmproxy.exceptions import ProtocolException, TlsProtocolException from netlib.exceptions import TcpException from ..protocol import ( RawTCPLayer, TlsLayer, Http1Layer, Http2Layer, is_tls_record_magic, ServerConnectionMixin, - UpstreamConnectLayer + UpstreamConnectLayer, TlsClientHello ) 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. @@ -48,16 +49,25 @@ class RootContext(object): return self.channel.ask("next_layer", layer) def _next_layer(self, top_layer): - # 1. Check for --ignore. - if self.config.check_ignore(top_layer.server_conn.address): - return RawTCPLayer(top_layer, logging=False) - try: d = top_layer.client_conn.rfile.peek(3) except TcpException as e: six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) client_tls = is_tls_record_magic(d) + # 1. check for --ignore + if self.config.check_ignore: + ignore = self.config.check_ignore(top_layer.server_conn.address) + if not ignore and client_tls: + try: + client_hello = TlsClientHello.from_client_conn(self.client_conn) + except TlsProtocolException as e: + self.log("Cannot parse Client Hello: %s" % repr(e), "error") + else: + ignore = self.config.check_ignore((client_hello.client_sni, 443)) + if ignore: + return RawTCPLayer(top_layer, logging=False) + # 2. Always insert a TLS layer, even if there's neither client nor server tls. # An inline script may upgrade from http to https, # in which case we need some form of TLS layer. @@ -123,6 +133,7 @@ class RootContext(object): class Log(object): + def __init__(self, msg, level="info"): self.msg = msg self.level = level diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index 8917f99a..750cb1a4 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -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""" diff --git a/libmproxy/script/__init__.py b/libmproxy/script/__init__.py index 8bcdc5a2..3ee19b04 100644 --- a/libmproxy/script/__init__.py +++ b/libmproxy/script/__init__.py @@ -10,4 +10,4 @@ __all__ = [ "concurrent", "ScriptException", "reloader" -]
\ No newline at end of file +] diff --git a/libmproxy/script/concurrent.py b/libmproxy/script/concurrent.py index bee2d43b..f0f5e3cd 100644 --- a/libmproxy/script/concurrent.py +++ b/libmproxy/script/concurrent.py @@ -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 diff --git a/libmproxy/script/reloader.py b/libmproxy/script/reloader.py index e81bdef6..b4acf51b 100644 --- a/libmproxy/script/reloader.py +++ b/libmproxy/script/reloader.py @@ -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"] - diff --git a/libmproxy/script/script.py b/libmproxy/script/script.py index 498caf94..a4ed3aba 100644 --- a/libmproxy/script/script.py +++ b/libmproxy/script/script.py @@ -3,7 +3,7 @@ The script object representing mitmproxy inline scripts. Script objects know nothing about mitmproxy or mitmproxy's API - this knowledge is provided by the mitmproxy-specific ScriptContext. """ -from __future__ import absolute_import, print_function, division +# Do not import __future__ here, this would apply transitively to the inline scripts. import os import shlex import traceback @@ -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
\ No newline at end of file + return None diff --git a/libmproxy/script/script_context.py b/libmproxy/script/script_context.py index d8748cb2..cd5d4b61 100644 --- a/libmproxy/script/script_context.py +++ b/libmproxy/script/script_context.py @@ -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. diff --git a/libmproxy/stateobject.py b/libmproxy/stateobject.py index 53ccef0e..52a8347f 100644 --- a/libmproxy/stateobject.py +++ b/libmproxy/stateobject.py @@ -2,6 +2,7 @@ from __future__ import absolute_import class StateObject(object): + """ An object with serializable state. diff --git a/libmproxy/utils.py b/libmproxy/utils.py index 8bd843a0..a697a637 100644 --- a/libmproxy/utils.py +++ b/libmproxy/utils.py @@ -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. """ diff --git a/libmproxy/web/__init__.py b/libmproxy/web/__init__.py index 90da6ffe..43fc993d 100644 --- a/libmproxy/web/__init__.py +++ b/libmproxy/web/__init__.py @@ -2,7 +2,6 @@ from __future__ import absolute_import, print_function import collections import tornado.ioloop import tornado.httpserver -import os from .. import controller, flow from . import app @@ -12,6 +11,7 @@ class Stop(Exception): class WebFlowView(flow.FlowView): + def __init__(self, store): super(WebFlowView, self).__init__(store, None) @@ -48,6 +48,7 @@ class WebFlowView(flow.FlowView): class WebState(flow.State): + def __init__(self): super(WebState, self).__init__() self.view._close() @@ -122,6 +123,7 @@ class Options(object): class WebMaster(flow.FlowMaster): + def __init__(self, server, options): self.options = options super(WebMaster, self).__init__(server, WebState()) diff --git a/libmproxy/web/app.py b/libmproxy/web/app.py index 5c80584d..79f76013 100644 --- a/libmproxy/web/app.py +++ b/libmproxy/web/app.py @@ -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 = [ |