aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/install.rst24
-rw-r--r--mitmproxy/contrib/tnetstring.py (renamed from mitmproxy/tnetstring.py)280
-rw-r--r--mitmproxy/flow/io.py2
-rw-r--r--mitmproxy/platform/pf.py1
-rw-r--r--mitmproxy/proxy/config.py2
-rw-r--r--mitmproxy/web/static/app.js11
-rw-r--r--netlib/http/http2/framereader.py3
-rw-r--r--test/mitmproxy/test_contrib_tnetstring.py141
-rw-r--r--test/mitmproxy/test_flow.py3
-rw-r--r--test/mitmproxy/test_protocol_http1.py8
-rw-r--r--test/mitmproxy/test_protocol_http2.py41
-rw-r--r--tox.ini2
-rw-r--r--web/src/js/ducks/settings.js2
13 files changed, 342 insertions, 178 deletions
diff --git a/docs/install.rst b/docs/install.rst
index 566430f5..6d82f81f 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -11,8 +11,10 @@ Installation On Ubuntu
Ubuntu comes with Python but we need to install pip, python-dev and several libraries.
This was tested on a fully patched installation of Ubuntu 14.04.
->>> sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev
->>> sudo pip install mitmproxy
+.. code:: bash
+
+ sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev
+ sudo pip install mitmproxy # or pip install --user mitmproxy
Once installation is complete you can run :ref:`mitmproxy` or :ref:`mitmdump` from a terminal.
@@ -27,6 +29,20 @@ get set up to contribute to the project, install the dependencies as you would f
mitmproxy installation (see :ref:`install-ubuntu`).
Then see the Hacking_ section of the README on GitHub.
+.. _install-fedora:
+
+Installation On Fedora
+----------------------
+
+Fedora comes with Python but we need to install pip, python-dev and several libraries.
+This was tested on a fully patched installation of Fedora 23.
+
+.. code:: bash
+
+ sudo dnf install -y python-pip python-devel libffi-devel openssl-devel libxml2-devel libxslt-devel libpng-devel libjpeg-devel
+ sudo pip install mitmproxy # or pip install --user mitmproxy
+
+Once installation is complete you can run :ref:`mitmproxy` or :ref:`mitmdump` from a terminal.
.. _install-arch:
@@ -87,7 +103,9 @@ Installation On Windows
First, install the latest version of Python 2.7 from the `Python website`_.
If you already have an older version of Python 2.7 installed, make sure to install pip_
-(pip is included in Python 2.7.9+ by default).
+(pip is included in Python 2.7.9+ by default). If pip aborts with an error, make sure you are using the current version of pip.
+
+>>> python -m pip install --upgrade pip
Next, add Python and the Python Scripts directory to your **PATH** variable.
You can do this easily by running the following in powershell:
diff --git a/mitmproxy/tnetstring.py b/mitmproxy/contrib/tnetstring.py
index f40e8ad8..9bf20b09 100644
--- a/mitmproxy/tnetstring.py
+++ b/mitmproxy/contrib/tnetstring.py
@@ -79,9 +79,8 @@ __version__ = "%d.%d.%d%s" % (
__ver_major__, __ver_minor__, __ver_patch__, __ver_sub__)
-def dumps(value, encoding=None):
- """dumps(object,encoding=None) -> string
-
+def dumps(value):
+ """
This function dumps a python object as a tnetstring.
"""
# This uses a deque to collect output fragments in reverse order,
@@ -91,22 +90,21 @@ def dumps(value, encoding=None):
# consider the _gdumps() function instead; it's a standard top-down
# generator that's simpler to understand but much less efficient.
q = deque()
- _rdumpq(q, 0, value, encoding)
- return "".join(q)
-
+ _rdumpq(q, 0, value)
+ return b''.join(q)
-def dump(value, file, encoding=None):
- """dump(object,file,encoding=None)
- This function dumps a python object as a tnetstring and writes it to
- the given file.
+def dump(value, file_handle):
"""
- file.write(dumps(value, encoding))
- file.flush()
+ This function dumps a python object as a tnetstring and
+ writes it to the given file.
+ """
+ file_handle.write(dumps(value))
-def _rdumpq(q, size, value, encoding=None):
- """Dump value as a tnetstring, to a deque instance, last chunks first.
+def _rdumpq(q, size, value):
+ """
+ Dump value as a tnetstring, to a deque instance, last chunks first.
This function generates the tnetstring representation of the given value,
pushing chunks of the output onto the given deque instance. It pushes
@@ -122,79 +120,70 @@ def _rdumpq(q, size, value, encoding=None):
"""
write = q.appendleft
if value is None:
- write("0:~")
+ write(b'0:~')
return size + 3
- if value is True:
- write("4:true!")
+ elif value is True:
+ write(b'4:true!')
return size + 7
- if value is False:
- write("5:false!")
+ elif value is False:
+ write(b'5:false!')
return size + 8
- if isinstance(value, six.integer_types):
- data = str(value)
+ elif isinstance(value, six.integer_types):
+ data = str(value).encode()
ldata = len(data)
- span = str(ldata)
- write("#")
+ span = str(ldata).encode()
+ write(b'#')
write(data)
- write(":")
+ write(b':')
write(span)
return size + 2 + len(span) + ldata
- if isinstance(value, (float,)):
+ elif isinstance(value, float):
# Use repr() for float rather than str().
# It round-trips more accurately.
# Probably unnecessary in later python versions that
# use David Gay's ftoa routines.
- data = repr(value)
+ data = repr(value).encode()
ldata = len(data)
- span = str(ldata)
- write("^")
+ span = str(ldata).encode()
+ write(b'^')
write(data)
- write(":")
+ write(b':')
write(span)
return size + 2 + len(span) + ldata
- if isinstance(value, str):
+ elif isinstance(value, bytes):
lvalue = len(value)
- span = str(lvalue)
- write(",")
+ span = str(lvalue).encode()
+ write(b',')
write(value)
- write(":")
+ write(b':')
write(span)
return size + 2 + len(span) + lvalue
- if isinstance(value, (list, tuple,)):
- write("]")
+ elif isinstance(value, (list, tuple)):
+ write(b']')
init_size = size = size + 1
for item in reversed(value):
- size = _rdumpq(q, size, item, encoding)
- span = str(size - init_size)
- write(":")
+ size = _rdumpq(q, size, item)
+ span = str(size - init_size).encode()
+ write(b':')
write(span)
return size + 1 + len(span)
- if isinstance(value, dict):
- write("}")
+ elif isinstance(value, dict):
+ write(b'}')
init_size = size = size + 1
- for (k, v) in six.iteritems(value):
- size = _rdumpq(q, size, v, encoding)
- size = _rdumpq(q, size, k, encoding)
- span = str(size - init_size)
- write(":")
+ for (k, v) in value.items():
+ size = _rdumpq(q, size, v)
+ size = _rdumpq(q, size, k)
+ span = str(size - init_size).encode()
+ write(b':')
write(span)
return size + 1 + len(span)
- if isinstance(value, unicode):
- if encoding is None:
- raise ValueError("must specify encoding to dump unicode strings")
- value = value.encode(encoding)
- lvalue = len(value)
- span = str(lvalue)
- write(",")
- write(value)
- write(":")
- write(span)
- return size + 2 + len(span) + lvalue
- raise ValueError("unserializable object")
+ else:
+ raise ValueError("unserializable object: {} ({})".format(value, type(value)))
-def _gdumps(value, encoding):
- """Generate fragments of value dumped as a tnetstring.
+def _gdumps(value):
+ """
+ Generate fragments of value dumped as a tnetstring.
This is the naive dumping algorithm, implemented as a generator so that
it's easy to pass to "".join() without building a new list.
@@ -203,72 +192,63 @@ def _gdumps(value, encoding):
measurably faster as it doesn't have to build intermediate strins.
"""
if value is None:
- yield "0:~"
+ yield b'0:~'
elif value is True:
- yield "4:true!"
+ yield b'4:true!'
elif value is False:
- yield "5:false!"
+ yield b'5:false!'
elif isinstance(value, six.integer_types):
- data = str(value)
- yield str(len(data))
- yield ":"
+ data = str(value).encode()
+ yield str(len(data)).encode()
+ yield b':'
yield data
- yield "#"
- elif isinstance(value, (float,)):
- data = repr(value)
- yield str(len(data))
- yield ":"
+ yield b'#'
+ elif isinstance(value, float):
+ data = repr(value).encode()
+ yield str(len(data)).encode()
+ yield b':'
yield data
- yield "^"
- elif isinstance(value, (str,)):
- yield str(len(value))
- yield ":"
+ yield b'^'
+ elif isinstance(value, bytes):
+ yield str(len(value)).encode()
+ yield b':'
yield value
- yield ","
- elif isinstance(value, (list, tuple,)):
+ yield b','
+ elif isinstance(value, (list, tuple)):
sub = []
for item in value:
sub.extend(_gdumps(item))
- sub = "".join(sub)
- yield str(len(sub))
- yield ":"
+ sub = b''.join(sub)
+ yield str(len(sub)).encode()
+ yield b':'
yield sub
- yield "]"
+ yield b']'
elif isinstance(value, (dict,)):
sub = []
- for (k, v) in six.iteritems(value):
+ for (k, v) in value.items():
sub.extend(_gdumps(k))
sub.extend(_gdumps(v))
- sub = "".join(sub)
- yield str(len(sub))
- yield ":"
+ sub = b''.join(sub)
+ yield str(len(sub)).encode()
+ yield b':'
yield sub
- yield "}"
- elif isinstance(value, (unicode,)):
- if encoding is None:
- raise ValueError("must specify encoding to dump unicode strings")
- value = value.encode(encoding)
- yield str(len(value))
- yield ":"
- yield value
- yield ","
+ yield b'}'
else:
raise ValueError("unserializable object")
-def loads(string, encoding=None):
- """loads(string,encoding=None) -> object
-
+def loads(string):
+ """
This function parses a tnetstring into a python object.
"""
# No point duplicating effort here. In the C-extension version,
# loads() is measurably faster then pop() since it can avoid
# the overhead of building a second string.
- return pop(string, encoding)[0]
+ return pop(string)[0]
-def load(file, encoding=None):
- """load(file,encoding=None) -> object
+def load(file_handle):
+ """load(file) -> object
This function reads a tnetstring from a file and parses it into a
python object. The file must support the read() method, and this
@@ -276,70 +256,68 @@ def load(file, encoding=None):
"""
# Read the length prefix one char at a time.
# Note that the netstring spec explicitly forbids padding zeros.
- c = file.read(1)
+ c = file_handle.read(1)
if not c.isdigit():
raise ValueError("not a tnetstring: missing or invalid length prefix")
- datalen = ord(c) - ord("0")
- c = file.read(1)
+ datalen = ord(c) - ord('0')
+ c = file_handle.read(1)
if datalen != 0:
while c.isdigit():
- datalen = (10 * datalen) + (ord(c) - ord("0"))
+ datalen = (10 * datalen) + (ord(c) - ord('0'))
if datalen > 999999999:
errmsg = "not a tnetstring: absurdly large length prefix"
raise ValueError(errmsg)
- c = file.read(1)
- if c != ":":
+ c = file_handle.read(1)
+ if c != b':':
raise ValueError("not a tnetstring: missing or invalid length prefix")
# Now we can read and parse the payload.
# This repeats the dispatch logic of pop() so we can avoid
# re-constructing the outermost tnetstring.
- data = file.read(datalen)
+ data = file_handle.read(datalen)
if len(data) != datalen:
raise ValueError("not a tnetstring: length prefix too big")
- type = file.read(1)
- if type == ",":
- if encoding is not None:
- return data.decode(encoding)
+ tns_type = file_handle.read(1)
+ if tns_type == b',':
return data
- if type == "#":
+ if tns_type == b'#':
try:
return int(data)
except ValueError:
raise ValueError("not a tnetstring: invalid integer literal")
- if type == "^":
+ if tns_type == b'^':
try:
return float(data)
except ValueError:
raise ValueError("not a tnetstring: invalid float literal")
- if type == "!":
- if data == "true":
+ if tns_type == b'!':
+ if data == b'true':
return True
- elif data == "false":
+ elif data == b'false':
return False
else:
raise ValueError("not a tnetstring: invalid boolean literal")
- if type == "~":
+ if tns_type == b'~':
if data:
raise ValueError("not a tnetstring: invalid null literal")
return None
- if type == "]":
+ if tns_type == b']':
l = []
while data:
- (item, data) = pop(data, encoding)
+ item, data = pop(data)
l.append(item)
return l
- if type == "}":
+ if tns_type == b'}':
d = {}
while data:
- (key, data) = pop(data, encoding)
- (val, data) = pop(data, encoding)
+ key, data = pop(data)
+ val, data = pop(data)
d[key] = val
return d
raise ValueError("unknown type tag")
-def pop(string, encoding=None):
- """pop(string,encoding=None) -> (object, remain)
+def pop(string):
+ """pop(string,encoding='utf_8') -> (object, remain)
This function parses a tnetstring into a python object.
It returns a tuple giving the parsed object and a string
@@ -347,53 +325,51 @@ def pop(string, encoding=None):
"""
# Parse out data length, type and remaining string.
try:
- (dlen, rest) = string.split(":", 1)
+ dlen, rest = string.split(b':', 1)
dlen = int(dlen)
except ValueError:
- raise ValueError("not a tnetstring: missing or invalid length prefix")
+ raise ValueError("not a tnetstring: missing or invalid length prefix: {}".format(string))
try:
- (data, type, remain) = (rest[:dlen], rest[dlen], rest[dlen + 1:])
+ data, tns_type, remain = rest[:dlen], rest[dlen:dlen + 1], rest[dlen + 1:]
except IndexError:
# This fires if len(rest) < dlen, meaning we don't need
# to further validate that data is the right length.
- raise ValueError("not a tnetstring: invalid length prefix")
+ raise ValueError("not a tnetstring: invalid length prefix: {}".format(dlen))
# Parse the data based on the type tag.
- if type == ",":
- if encoding is not None:
- return (data.decode(encoding), remain)
- return (data, remain)
- if type == "#":
+ if tns_type == b',':
+ return data, remain
+ if tns_type == b'#':
try:
- return (int(data), remain)
+ return int(data), remain
except ValueError:
- raise ValueError("not a tnetstring: invalid integer literal")
- if type == "^":
+ raise ValueError("not a tnetstring: invalid integer literal: {}".format(data))
+ if tns_type == b'^':
try:
- return (float(data), remain)
+ return float(data), remain
except ValueError:
- raise ValueError("not a tnetstring: invalid float literal")
- if type == "!":
- if data == "true":
- return (True, remain)
- elif data == "false":
- return (False, remain)
+ raise ValueError("not a tnetstring: invalid float literal: {}".format(data))
+ if tns_type == b'!':
+ if data == b'true':
+ return True, remain
+ elif data == b'false':
+ return False, remain
else:
- raise ValueError("not a tnetstring: invalid boolean literal")
- if type == "~":
+ raise ValueError("not a tnetstring: invalid boolean literal: {}".format(data))
+ if tns_type == b'~':
if data:
raise ValueError("not a tnetstring: invalid null literal")
- return (None, remain)
- if type == "]":
+ return None, remain
+ if tns_type == b']':
l = []
while data:
- (item, data) = pop(data, encoding)
+ item, data = pop(data)
l.append(item)
return (l, remain)
- if type == "}":
+ if tns_type == b'}':
d = {}
while data:
- (key, data) = pop(data, encoding)
- (val, data) = pop(data, encoding)
+ key, data = pop(data)
+ val, data = pop(data)
d[key] = val
- return (d, remain)
- raise ValueError("unknown type tag")
+ return d, remain
+ raise ValueError("unknown type tag: {}".format(tns_type))
diff --git a/mitmproxy/flow/io.py b/mitmproxy/flow/io.py
index cd3d9986..671ddf43 100644
--- a/mitmproxy/flow/io.py
+++ b/mitmproxy/flow/io.py
@@ -4,7 +4,7 @@ import os
from mitmproxy import exceptions
from mitmproxy import models
-from mitmproxy import tnetstring
+from mitmproxy.contrib import tnetstring
from mitmproxy.flow import io_compat
diff --git a/mitmproxy/platform/pf.py b/mitmproxy/platform/pf.py
index 97a4c192..c0397d78 100644
--- a/mitmproxy/platform/pf.py
+++ b/mitmproxy/platform/pf.py
@@ -8,6 +8,7 @@ def lookup(address, port, s):
Returns an (address, port) tuple, or None.
"""
+ s = s.decode()
spec = "%s:%s" % (address, port)
for i in s.split("\n"):
if "ESTABLISHED:ESTABLISHED" in i and spec in i:
diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py
index 9246fe04..32d881b0 100644
--- a/mitmproxy/proxy/config.py
+++ b/mitmproxy/proxy/config.py
@@ -199,7 +199,7 @@ def process_proxy_options(parser, options):
password_manager = authentication.PassManHtpasswd(
options.auth_htpasswd)
except ValueError as v:
- return parser.error(v.message)
+ return parser.error(v)
authenticator = authentication.BasicProxyAuth(password_manager, "mitmproxy")
else:
authenticator = authentication.NullProxyAuth(None)
diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js
index 06353edf..799ad03e 100644
--- a/mitmproxy/web/static/app.js
+++ b/mitmproxy/web/static/app.js
@@ -116,12 +116,21 @@
"use strict";function calcVScroll(t){if(!t)return{start:0,end:0,paddingTop:0,paddingBottom:0};var e=t.itemCount,o=t.rowHeight,r=t.viewportTop,a=t.viewportHeight,i=t.itemHeights,l=r+a,n=0,c=0,d=0,p=0;if(i)for(var h=0,s=0;e>h;h++){var m=i[h]||o;r>=s&&h%2===0&&(d=s,n=h),l>=s?c=h+1:p+=m,s+=m}else n=-2&Math.max(0,Math.floor(r/o)-1),c=Math.min(e,n+Math.ceil(a/o)+2),d=Math.min(n,e)*o,p=Math.max(0,e-c)*o;return{start:n,end:c,paddingTop:d,paddingBottom:p}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.calcVScroll=calcVScroll;
},{}],40:[function(require,module,exports){
+<<<<<<< HEAD
+"use strict";function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function Connection(e,t){"/"===e[0]&&(e=location.origin.replace("http","ws")+e);var n=new WebSocket(e);return n.onopen=function(){t(webSocketActions.connected()),t(settingsActions.fetchSettings()),t(flowActions.fetchFlows()).then(function(){console.log("flows are loaded now"),_actions.ConnectionActions.open()}),t(eventLogActions.fetchLogEntries())},n.onmessage=function(e){var n=JSON.parse(e.data);switch(_dispatcher.AppDispatcher.dispatchServerAction(n),n.type){case eventLogActions.UPDATE_LOG:return t(eventLogActions.updateLogEntries(n));case flowActions.UPDATE_FLOWS:return t(flowActions.updateFlows(n));case settingsActions.UPDATE_SETTINGS:return t(settingsActions.handleWsMsg(n));default:console.warn("unknown message",n)}},n.onerror=function(){_actions.ConnectionActions.error(),t(eventLogActions.addLogEntry("WebSocket connection error."))},n.onclose=function(){_actions.ConnectionActions.close(),t(eventLogActions.addLogEntry("WebSocket connection closed.")),t(webSocketActions.disconnected())},n}Object.defineProperty(exports,"__esModule",{value:!0}),exports["default"]=Connection;var _actions=require("./actions.js"),_dispatcher=require("./dispatcher.js"),_websocket=require("./ducks/websocket"),webSocketActions=_interopRequireWildcard(_websocket),_eventLog=require("./ducks/eventLog"),eventLogActions=_interopRequireWildcard(_eventLog),_flows=require("./ducks/flows"),flowActions=_interopRequireWildcard(_flows),_settings=require("./ducks/settings"),settingsActions=_interopRequireWildcard(_settings);
+
+},{"./actions.js":1,"./dispatcher.js":41,"./ducks/eventLog":42,"./ducks/flows":43,"./ducks/settings":45,"./ducks/websocket":49}],41:[function(require,module,exports){
+"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.AppDispatcher=void 0;var _flux=require("flux"),_flux2=_interopRequireDefault(_flux),PayloadSources={VIEW:"view",SERVER:"server"},AppDispatcher=exports.AppDispatcher=new _flux2["default"].Dispatcher;AppDispatcher.dispatchViewAction=function(e){e.source=PayloadSources.VIEW,this.dispatch(e)},AppDispatcher.dispatchServerAction=function(e){e.source=PayloadSources.SERVER,this.dispatch(e)};
+
+},{"flux":"flux"}],42:[function(require,module,exports){
+=======
"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.AppDispatcher=void 0;var _flux=require("flux"),_flux2=_interopRequireDefault(_flux),PayloadSources={VIEW:"view",SERVER:"server"},AppDispatcher=exports.AppDispatcher=new _flux2["default"].Dispatcher;AppDispatcher.dispatchViewAction=function(e){e.source=PayloadSources.VIEW,this.dispatch(e)},AppDispatcher.dispatchServerAction=function(e){e.source=PayloadSources.SERVER,this.dispatch(e)};
},{"flux":"flux"}],41:[function(require,module,exports){
"use strict";function reduce(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1];switch(t.type){default:return e}}function init(){return function(e){e((0,_websocket.connect)()),e({type:INIT})}}function destruct(){return function(e){e((0,_websocket.disconnect)()),e({type:DESTRUCT})}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.INIT=void 0,exports.reduce=reduce,exports.init=init,exports.destruct=destruct;var _websocket=require("./websocket"),INIT=exports.INIT="APP_INIT",defaultState={};
},{"./websocket":49}],42:[function(require,module,exports){
+>>>>>>> jason/websocket
"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}function _defineProperty(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function reducer(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1],r=function(){switch(t.type){case TOGGLE_FILTER:var r=_extends({},e.filter,_defineProperty({},t.filter,!e.filter[t.filter]));return{v:_extends({},e,{filter:r,filteredEvents:(0,_view.updateViewFilter)(e.events,function(e){return r[e.level]})})};case TOGGLE_VISIBILITY:return{v:_extends({},e,{visible:!e.visible})};case UPDATE_LOG:var i=reduceList(e.events,t);return{v:_extends({},e,{events:i,filteredEvents:(0,_view.updateViewList)(e.filteredEvents,e.events,i,t,function(t){return e.filter[t.level]})})};default:return{v:e}}}();return"object"===("undefined"==typeof r?"undefined":_typeof(r))?r.v:void 0}function toggleEventLogFilter(e){return{type:TOGGLE_FILTER,filter:e}}function toggleEventLogVisibility(){return{type:TOGGLE_VISIBILITY}}function addLogEntry(e){var t=arguments.length<=1||void 0===arguments[1]?"web":arguments[1];return addItem({message:e,level:t,id:"log-"+id++})}Object.defineProperty(exports,"__esModule",{value:!0}),exports.fetchLogEntries=exports.updateLogEntries=exports.UPDATE_LOG=void 0;var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},_extends=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i])}return e};exports["default"]=reducer,exports.toggleEventLogFilter=toggleEventLogFilter,exports.toggleEventLogVisibility=toggleEventLogVisibility,exports.addLogEntry=addLogEntry;var _list=require("./utils/list"),_list2=_interopRequireDefault(_list),_view=require("./utils/view"),TOGGLE_FILTER="TOGGLE_EVENTLOG_FILTER",TOGGLE_VISIBILITY="TOGGLE_EVENTLOG_VISIBILITY",UPDATE_LOG=exports.UPDATE_LOG="UPDATE_EVENTLOG",_makeList=(0,_list2["default"])(UPDATE_LOG,"/events"),reduceList=_makeList.reduceList,updateList=_makeList.updateList,fetchList=_makeList.fetchList,addItem=_makeList.addItem,defaultState={visible:!1,filter:{debug:!1,info:!0,web:!0},events:reduceList(),filteredEvents:[]},id=0;exports.updateLogEntries=updateList,exports.fetchLogEntries=fetchList;
},{"./utils/list":47,"./utils/view":48}],43:[function(require,module,exports){
@@ -131,7 +140,7 @@
"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(exports,"__esModule",{value:!0});var _redux=require("redux"),_eventLog=require("./eventLog"),_eventLog2=_interopRequireDefault(_eventLog),_websocket=require("./websocket"),_websocket2=_interopRequireDefault(_websocket),_flows=require("./flows"),_flows2=_interopRequireDefault(_flows),_settings=require("./settings"),_settings2=_interopRequireDefault(_settings),_ui=require("./ui"),_ui2=_interopRequireDefault(_ui),rootReducer=(0,_redux.combineReducers)({eventLog:_eventLog2["default"],websocket:_websocket2["default"],flows:_flows2["default"],settings:_settings2["default"],ui:_ui2["default"]});exports["default"]=rootReducer;
},{"./eventLog":42,"./flows":43,"./settings":45,"./ui":46,"./websocket":49,"redux":"redux"}],45:[function(require,module,exports){
-"use strict";function _toConsumableArray(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}function reducer(){var t=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],e=arguments[1];switch(e.type){case REQUEST_SETTINGS:return _extends({},t,{isFetching:!0});case RECEIVE_SETTINGS:var n={settings:e.settings,isFetching:!1,actionsDuringFetch:[]},r=!0,s=!1,i=void 0;try{for(var u,E=t.actionsDuringFetch[Symbol.iterator]();!(r=(u=E.next()).done);r=!0)e=u.value,n=reducer(n,e)}catch(S){s=!0,i=S}finally{try{!r&&E["return"]&&E["return"]()}finally{if(s)throw i}}return n;case UPDATE_SETTINGS:return t.isFetching?_extends({},t,{actionsDuringFetch:[].concat(_toConsumableArray(t.actionsDuringFetch),[e])}):_extends({},t,{settings:_extends({},t.settings,e.settings)});default:return t}}function updateSettings(t){return"update"===t.cmd?{type:UPDATE_SETTINGS,settings:t.data}:void console.error("unknown settings update",t)}function fetchSettings(){return function(t){return t({type:REQUEST_SETTINGS}),(0,_utils.fetchApi)("/settings").then(function(t){return t.json()}).then(function(e){return t({type:RECEIVE_SETTINGS,settings:e.data})})}}function updateSettings(t){return _utils.fetchApi.put("/settings",t),{type:SET_INTERCEPT}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.UPDATE_SETTINGS=exports.RECEIVE_SETTINGS=exports.REQUEST_SETTINGS=void 0;var _extends=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};exports["default"]=reducer,exports.updateSettings=updateSettings,exports.fetchSettings=fetchSettings,exports.updateSettings=updateSettings;var _utils=require("../utils"),REQUEST_SETTINGS=exports.REQUEST_SETTINGS="REQUEST_SETTINGS",RECEIVE_SETTINGS=exports.RECEIVE_SETTINGS="RECEIVE_SETTINGS",UPDATE_SETTINGS=exports.UPDATE_SETTINGS="UPDATE_SETTINGS",defaultState={settings:{},isFetching:!1,actionsDuringFetch:[]};
+"use strict";function _toConsumableArray(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);e<t.length;e++)n[e]=t[e];return n}return Array.from(t)}function reducer(){var t=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],e=arguments[1];switch(e.type){case REQUEST_SETTINGS:return _extends({},t,{isFetching:!0});case RECEIVE_SETTINGS:var n={settings:e.settings,isFetching:!1,actionsDuringFetch:[]},r=!0,s=!1,i=void 0;try{for(var E,u=t.actionsDuringFetch[Symbol.iterator]();!(r=(E=u.next()).done);r=!0)e=E.value,n=reducer(n,e)}catch(a){s=!0,i=a}finally{try{!r&&u["return"]&&u["return"]()}finally{if(s)throw i}}return n;case UPDATE_SETTINGS:return t.isFetching?_extends({},t,{actionsDuringFetch:[].concat(_toConsumableArray(t.actionsDuringFetch),[e])}):_extends({},t,{settings:_extends({},t.settings,e.settings)});default:return t}}function handleWsMsg(t){return"update"===t.cmd?{type:UPDATE_SETTINGS,settings:t.data}:void console.error("unknown settings update",t)}function fetchSettings(){return function(t){return t({type:REQUEST_SETTINGS}),(0,_utils.fetchApi)("/settings").then(function(t){return t.json()}).then(function(e){return t({type:RECEIVE_SETTINGS,settings:e.data})})}}function updateSettings(t){return _utils.fetchApi.put("/settings",t),{type:SET_INTERCEPT}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.UPDATE_SETTINGS=exports.RECEIVE_SETTINGS=exports.REQUEST_SETTINGS=void 0;var _extends=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};exports["default"]=reducer,exports.handleWsMsg=handleWsMsg,exports.fetchSettings=fetchSettings,exports.updateSettings=updateSettings;var _utils=require("../utils"),REQUEST_SETTINGS=exports.REQUEST_SETTINGS="REQUEST_SETTINGS",RECEIVE_SETTINGS=exports.RECEIVE_SETTINGS="RECEIVE_SETTINGS",UPDATE_SETTINGS=exports.UPDATE_SETTINGS="UPDATE_SETTINGS",defaultState={settings:{},isFetching:!1,actionsDuringFetch:[]};
},{"../utils":52}],46:[function(require,module,exports){
"use strict";function reducer(){var e=arguments.length<=0||void 0===arguments[0]?defaultState:arguments[0],t=arguments[1];switch(t.type){case SET_ACTIVE_MENU:return _extends({},e,{activeMenu:t.activeMenu});case _flows.SELECT_FLOW:var r=t.flowId&&!t.currentSelection,n=!t.flowId&&t.currentSelection;return r?_extends({},e,{activeMenu:"Flow"}):n&&"Flow"===e.activeMenu?_extends({},e,{activeMenu:"Start"}):e;default:return e}}function setActiveMenu(e){return{type:SET_ACTIVE_MENU,activeMenu:e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.SET_ACTIVE_MENU=void 0;var _extends=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e};exports["default"]=reducer,exports.setActiveMenu=setActiveMenu;var _flows=require("./flows"),SET_ACTIVE_MENU=exports.SET_ACTIVE_MENU="SET_ACTIVE_MENU",defaultState={activeMenu:"Start"};
diff --git a/netlib/http/http2/framereader.py b/netlib/http/http2/framereader.py
index d45be646..eb9b069a 100644
--- a/netlib/http/http2/framereader.py
+++ b/netlib/http/http2/framereader.py
@@ -1,6 +1,7 @@
import codecs
import hyperframe
+from ...exceptions import HttpException
def http2_read_raw_frame(rfile):
@@ -8,7 +9,7 @@ def http2_read_raw_frame(rfile):
length = int(codecs.encode(header[:3], 'hex_codec'), 16)
if length == 4740180:
- raise ValueError("Length field looks more like HTTP/1.1: %s" % rfile.peek(20))
+ raise HttpException("Length field looks more like HTTP/1.1:\n{}".format(rfile.read(-1)))
body = rfile.safe_read(length)
return [header, body]
diff --git a/test/mitmproxy/test_contrib_tnetstring.py b/test/mitmproxy/test_contrib_tnetstring.py
new file mode 100644
index 00000000..17654ad9
--- /dev/null
+++ b/test/mitmproxy/test_contrib_tnetstring.py
@@ -0,0 +1,141 @@
+import unittest
+import random
+import math
+import io
+import struct
+
+from mitmproxy.contrib import tnetstring
+
+MAXINT = 2 ** (struct.Struct('i').size * 8 - 1) - 1
+
+FORMAT_EXAMPLES = {
+ b'0:}': {},
+ b'0:]': [],
+ b'51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00\x00,]}':
+ {b'hello': [12345678901, b'this', True, None, b'\x00\x00\x00\x00']},
+ b'5:12345#': 12345,
+ b'12:this is cool,': b'this is cool',
+ b'0:,': b'',
+ b'0:~': None,
+ b'4:true!': True,
+ b'5:false!': False,
+ b'10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+ b'24:5:12345#5:67890#5:xxxxx,]': [12345, 67890, b'xxxxx'],
+ b'18:3:0.1^3:0.2^3:0.3^]': [0.1, 0.2, 0.3],
+ b'243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]': [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[b'hello-there']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] # noqa
+}
+
+
+def get_random_object(random=random, depth=0):
+ """Generate a random serializable object."""
+ # The probability of generating a scalar value increases as the depth increase.
+ # This ensures that we bottom out eventually.
+ if random.randint(depth, 10) <= 4:
+ what = random.randint(0, 1)
+ if what == 0:
+ n = random.randint(0, 10)
+ l = []
+ for _ in range(n):
+ l.append(get_random_object(random, depth + 1))
+ return l
+ if what == 1:
+ n = random.randint(0, 10)
+ d = {}
+ for _ in range(n):
+ n = random.randint(0, 100)
+ k = bytes([random.randint(32, 126) for _ in range(n)])
+ d[k] = get_random_object(random, depth + 1)
+ return d
+ else:
+ what = random.randint(0, 4)
+ if what == 0:
+ return None
+ if what == 1:
+ return True
+ if what == 2:
+ return False
+ if what == 3:
+ if random.randint(0, 1) == 0:
+ return random.randint(0, MAXINT)
+ else:
+ return -1 * random.randint(0, MAXINT)
+ n = random.randint(0, 100)
+ return bytes([random.randint(32, 126) for _ in range(n)])
+
+
+class Test_Format(unittest.TestCase):
+
+ def test_roundtrip_format_examples(self):
+ for data, expect in FORMAT_EXAMPLES.items():
+ self.assertEqual(expect, tnetstring.loads(data))
+ self.assertEqual(
+ expect, tnetstring.loads(tnetstring.dumps(expect)))
+ self.assertEqual((expect, b''), tnetstring.pop(data))
+
+ def test_roundtrip_format_random(self):
+ for _ in range(500):
+ v = get_random_object()
+ self.assertEqual(v, tnetstring.loads(tnetstring.dumps(v)))
+ self.assertEqual((v, b""), tnetstring.pop(tnetstring.dumps(v)))
+
+ def test_unicode_handling(self):
+ with self.assertRaises(ValueError):
+ tnetstring.dumps(u"hello")
+ self.assertEqual(tnetstring.dumps(u"hello".encode()), b"5:hello,")
+ self.assertEqual(type(tnetstring.loads(b"5:hello,")), bytes)
+
+ def test_roundtrip_format_unicode(self):
+ for _ in range(500):
+ v = get_random_object()
+ self.assertEqual(v, tnetstring.loads(tnetstring.dumps(v)))
+ self.assertEqual((v, b''), tnetstring.pop(tnetstring.dumps(v)))
+
+ def test_roundtrip_big_integer(self):
+ i1 = math.factorial(30000)
+ s = tnetstring.dumps(i1)
+ i2 = tnetstring.loads(s)
+ self.assertEqual(i1, i2)
+
+
+class Test_FileLoading(unittest.TestCase):
+
+ def test_roundtrip_file_examples(self):
+ for data, expect in FORMAT_EXAMPLES.items():
+ s = io.BytesIO()
+ s.write(data)
+ s.write(b'OK')
+ s.seek(0)
+ self.assertEqual(expect, tnetstring.load(s))
+ self.assertEqual(b'OK', s.read())
+ s = io.BytesIO()
+ tnetstring.dump(expect, s)
+ s.write(b'OK')
+ s.seek(0)
+ self.assertEqual(expect, tnetstring.load(s))
+ self.assertEqual(b'OK', s.read())
+
+ def test_roundtrip_file_random(self):
+ for _ in range(500):
+ v = get_random_object()
+ s = io.BytesIO()
+ tnetstring.dump(v, s)
+ s.write(b'OK')
+ s.seek(0)
+ self.assertEqual(v, tnetstring.load(s))
+ self.assertEqual(b'OK', s.read())
+
+ def test_error_on_absurd_lengths(self):
+ s = io.BytesIO()
+ s.write(b'1000000000:pwned!,')
+ s.seek(0)
+ with self.assertRaises(ValueError):
+ tnetstring.load(s)
+ self.assertEqual(s.read(1), b':')
+
+
+def suite():
+ loader = unittest.TestLoader()
+ suite = unittest.TestSuite()
+ suite.addTest(loader.loadTestsFromTestCase(Test_Format))
+ suite.addTest(loader.loadTestsFromTestCase(Test_FileLoading))
+ return suite
diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py
index af8256c4..9eaab9aa 100644
--- a/test/mitmproxy/test_flow.py
+++ b/test/mitmproxy/test_flow.py
@@ -5,7 +5,8 @@ import mock
import netlib.utils
from netlib.http import Headers
-from mitmproxy import filt, controller, tnetstring, flow
+from mitmproxy import filt, controller, flow
+from mitmproxy.contrib import tnetstring
from mitmproxy.exceptions import FlowReadException, ScriptException
from mitmproxy.models import Error
from mitmproxy.models import Flow
diff --git a/test/mitmproxy/test_protocol_http1.py b/test/mitmproxy/test_protocol_http1.py
index e0a57b4e..cf7bd598 100644
--- a/test/mitmproxy/test_protocol_http1.py
+++ b/test/mitmproxy/test_protocol_http1.py
@@ -18,14 +18,14 @@ class TestInvalidRequests(tservers.HTTPProxyTest):
p = self.pathoc()
r = p.request("connect:'%s:%s'" % ("127.0.0.1", self.server2.port))
assert r.status_code == 400
- assert "Invalid HTTP request form" in r.content
+ assert b"Invalid HTTP request form" in r.content
def test_relative_request(self):
p = self.pathoc_raw()
p.connect()
r = p.request("get:/p/200")
assert r.status_code == 400
- assert "Invalid HTTP request form" in r.content
+ assert b"Invalid HTTP request form" in r.content
class TestExpectHeader(tservers.HTTPProxyTest):
@@ -43,8 +43,8 @@ class TestExpectHeader(tservers.HTTPProxyTest):
)
client.wfile.flush()
- assert client.rfile.readline() == "HTTP/1.1 100 Continue\r\n"
- assert client.rfile.readline() == "\r\n"
+ assert client.rfile.readline() == b"HTTP/1.1 100 Continue\r\n"
+ assert client.rfile.readline() == b"\r\n"
client.wfile.write(b"0123456789abcdef\r\n")
client.wfile.flush()
diff --git a/test/mitmproxy/test_protocol_http2.py b/test/mitmproxy/test_protocol_http2.py
index 23072260..932c8df2 100644
--- a/test/mitmproxy/test_protocol_http2.py
+++ b/test/mitmproxy/test_protocol_http2.py
@@ -13,6 +13,7 @@ from mitmproxy.cmdline import APP_HOST, APP_PORT
import netlib
from ..netlib import tservers as netlib_tservers
+from netlib.exceptions import HttpException
from netlib.http.http2 import framereader
from . import tservers
@@ -50,6 +51,9 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase):
try:
raw = b''.join(framereader.http2_read_raw_frame(self.rfile))
events = h2_conn.receive_data(raw)
+ except HttpException:
+ print(traceback.format_exc())
+ assert False
except:
break
self.wfile.write(h2_conn.data_to_send())
@@ -60,9 +64,7 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase):
if not self.server.handle_server_event(event, h2_conn, self.rfile, self.wfile):
done = True
break
- except Exception as e:
- print(repr(e))
- print(traceback.format_exc())
+ except:
done = True
break
@@ -200,9 +202,12 @@ class TestSimple(_Http2TestBase, _Http2ServerBase):
done = False
while not done:
try:
- events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile)))
- except:
- break
+ raw = b''.join(framereader.http2_read_raw_frame(client.rfile))
+ events = h2_conn.receive_data(raw)
+ except HttpException:
+ print(traceback.format_exc())
+ assert False
+
client.wfile.write(h2_conn.data_to_send())
client.wfile.flush()
@@ -270,9 +275,12 @@ class TestWithBodies(_Http2TestBase, _Http2ServerBase):
done = False
while not done:
try:
- events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile)))
- except:
- break
+ raw = b''.join(framereader.http2_read_raw_frame(client.rfile))
+ events = h2_conn.receive_data(raw)
+ except HttpException:
+ print(traceback.format_exc())
+ assert False
+
client.wfile.write(h2_conn.data_to_send())
client.wfile.flush()
@@ -364,6 +372,9 @@ class TestPushPromise(_Http2TestBase, _Http2ServerBase):
try:
raw = b''.join(framereader.http2_read_raw_frame(client.rfile))
events = h2_conn.receive_data(raw)
+ except HttpException:
+ print(traceback.format_exc())
+ assert False
except:
break
client.wfile.write(h2_conn.data_to_send())
@@ -412,9 +423,12 @@ class TestPushPromise(_Http2TestBase, _Http2ServerBase):
responses = 0
while not done:
try:
- events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile)))
- except:
- break
+ raw = b''.join(framereader.http2_read_raw_frame(client.rfile))
+ events = h2_conn.receive_data(raw)
+ except HttpException:
+ print(traceback.format_exc())
+ assert False
+
client.wfile.write(h2_conn.data_to_send())
client.wfile.flush()
@@ -481,6 +495,9 @@ class TestConnectionLost(_Http2TestBase, _Http2ServerBase):
try:
raw = b''.join(framereader.http2_read_raw_frame(client.rfile))
h2_conn.receive_data(raw)
+ except HttpException:
+ print(traceback.format_exc())
+ assert False
except:
break
try:
diff --git a/tox.ini b/tox.ini
index 4837d5b5..db46a8aa 100644
--- a/tox.ini
+++ b/tox.ini
@@ -7,7 +7,7 @@ deps =
codecov>=2.0.5
passenv = CI TRAVIS_BUILD_ID TRAVIS TRAVIS_BRANCH TRAVIS_JOB_NUMBER TRAVIS_PULL_REQUEST TRAVIS_JOB_ID TRAVIS_REPO_SLUG TRAVIS_COMMIT
setenv =
- PY3TESTS = test/netlib test/pathod/ test/mitmproxy/script test/mitmproxy/test_contentview.py test/mitmproxy/test_custom_contentview.py test/mitmproxy/test_app.py test/mitmproxy/test_controller.py test/mitmproxy/test_fuzzing.py test/mitmproxy/test_script.py test/mitmproxy/test_web_app.py test/mitmproxy/test_utils.py test/mitmproxy/test_stateobject.py test/mitmproxy/test_cmdline.py
+ PY3TESTS = test/netlib test/pathod/ test/mitmproxy/script test/mitmproxy/test_contentview.py test/mitmproxy/test_custom_contentview.py test/mitmproxy/test_app.py test/mitmproxy/test_controller.py test/mitmproxy/test_fuzzing.py test/mitmproxy/test_script.py test/mitmproxy/test_web_app.py test/mitmproxy/test_utils.py test/mitmproxy/test_stateobject.py test/mitmproxy/test_cmdline.py test/mitmproxy/test_contrib_tnetstring.py test/mitmproxy/test_proxy.py test/mitmproxy/test_protocol_http1.py test/mitmproxy/test_platform_pf.py
[testenv:py27]
commands =
diff --git a/web/src/js/ducks/settings.js b/web/src/js/ducks/settings.js
index 3e6c8366..bef7c0ff 100644
--- a/web/src/js/ducks/settings.js
+++ b/web/src/js/ducks/settings.js
@@ -47,7 +47,7 @@ export default function reducer(state = defaultState, action) {
}
}
-export function updateSettings(event) {
+export function handleWsMsg(event) {
/* This action creator takes all WebSocket events */
if (event.cmd === 'update') {
return {