diff options
-rw-r--r-- | docs/Makefile | 2 | ||||
-rw-r--r-- | docs/dev/models.rst | 10 | ||||
-rw-r--r-- | docs/dev/testing.rst | 2 | ||||
-rw-r--r-- | docs/index.rst | 2 | ||||
-rw-r--r-- | docs/scripting/mitmproxy.rst | 6 | ||||
-rw-r--r-- | mitmproxy/console/flowdetailview.py | 2 | ||||
-rw-r--r-- | mitmproxy/flow.py | 14 | ||||
-rw-r--r-- | mitmproxy/flow_format_compat.py | 1 | ||||
-rw-r--r-- | mitmproxy/models/__init__.py | 3 | ||||
-rw-r--r-- | mitmproxy/models/connections.py | 46 | ||||
-rw-r--r-- | mitmproxy/models/http.py | 84 | ||||
-rw-r--r-- | netlib/http/request.py | 1 | ||||
-rw-r--r-- | netlib/tcp.py | 6 | ||||
-rw-r--r-- | test/mitmproxy/tutils.py | 2 |
14 files changed, 82 insertions, 99 deletions
diff --git a/docs/Makefile b/docs/Makefile index 74bcda53..b8255107 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -192,4 +192,4 @@ pseudoxml: @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." livehtml: - sphinx-autobuild -b html -z '../mitmproxy' -z '../../netlib/netlib' -r '___jb_(old|bak)___$$' $(ALLSPHINXOPTS) $(BUILDDIR)/html
\ No newline at end of file + sphinx-autobuild -b html -z '../mitmproxy' -r '___jb_(old|bak|tmp)___$$' $(ALLSPHINXOPTS) $(BUILDDIR)/html
\ No newline at end of file diff --git a/docs/dev/models.rst b/docs/dev/models.rst index 7a949941..2eac6783 100644 --- a/docs/dev/models.rst +++ b/docs/dev/models.rst @@ -1,7 +1,11 @@ .. _models: -Models -====== +Datastructures +============== + +.. automodule:: mitmproxy.models + :members: HTTPFlow, HTTPRequest, HTTPResponse + .. automodule:: netlib.http @@ -56,4 +60,4 @@ Models .. automodule:: mitmproxy.models :show-inheritance: - :members: HTTPFlow, Error, ClientConnection, ServerConnection
\ No newline at end of file + :members: Error, ServerConnection, ClientConnection
\ No newline at end of file diff --git a/docs/dev/testing.rst b/docs/dev/testing.rst index e3b86bf3..c9ed5164 100644 --- a/docs/dev/testing.rst +++ b/docs/dev/testing.rst @@ -10,7 +10,7 @@ suitable extension to the test suite. Our tests are written for the `py.test`_ or nose_ test frameworks. At the point where you send your pull request, a command like this: ->>> py.test -n 4 --cov mitmproxy +>>> py.test --cov mitmproxy --cov netlib Should give output something like this: diff --git a/docs/index.rst b/docs/index.rst index 4bca07d1..d3b6f434 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -52,6 +52,7 @@ :caption: Scripting scripting/inlinescripts + dev/models scripting/mitmproxy @@ -73,7 +74,6 @@ dev/protocols dev/proxy dev/exceptions - dev/models .. Indices and tables ================== diff --git a/docs/scripting/mitmproxy.rst b/docs/scripting/mitmproxy.rst index 73450df5..9e331736 100644 --- a/docs/scripting/mitmproxy.rst +++ b/docs/scripting/mitmproxy.rst @@ -1,10 +1,10 @@ -mitmproxy -========= +FlowMaster +========== .. note:: - We strongly encourage you to use :ref:`inlinescripts` rather than mitmproxy. + We strongly encourage you to use :ref:`inlinescripts` rather than subclassing mitmproxy's FlowMaster. - Inline Scripts are equally powerful and provide an easier syntax. - Most examples are written as inline scripts. - Multiple inline scripts can be used together. diff --git a/mitmproxy/console/flowdetailview.py b/mitmproxy/console/flowdetailview.py index d338b6bc..ca083b10 100644 --- a/mitmproxy/console/flowdetailview.py +++ b/mitmproxy/console/flowdetailview.py @@ -23,7 +23,7 @@ def flowdetails(state, flow): text.append(urwid.Text([("head", "Server Connection:")])) parts = [ ["Address", repr(sc.address)], - ["Peer Address", repr(sc.peer_address)], + ["Resolved Address", repr(sc.ip_address)], ] text.extend( diff --git a/mitmproxy/flow.py b/mitmproxy/flow.py index 246e9f82..cf07ea9a 100644 --- a/mitmproxy/flow.py +++ b/mitmproxy/flow.py @@ -842,21 +842,15 @@ class FlowMaster(controller.Master): """ this method creates a new artificial and minimalist request also adds it to flowlist """ - c = ClientConnection.from_state(dict( - address=dict(address=(host, port), use_ipv6=False), - clientcert=None, - ssl_established=False, - timestamp_start=None, - timestamp_end=None, - timestamp_ssl_setup=None - )) + c = ClientConnection.make_dummy(("", 0)) + s = ServerConnection.make_dummy((host, port)) s = ServerConnection.from_state(dict( address=dict(address=(host, port), use_ipv6=False), - peer_address=None, + ip_address=None, cert=None, sni=host, - source_address=dict(address=('', 0), use_ipv6=False), + source_address=dict(address=("", 0), use_ipv6=False), ssl_established=True, timestamp_start=None, timestamp_tcp_setup=None, diff --git a/mitmproxy/flow_format_compat.py b/mitmproxy/flow_format_compat.py index 84da3495..8478367c 100644 --- a/mitmproxy/flow_format_compat.py +++ b/mitmproxy/flow_format_compat.py @@ -41,6 +41,7 @@ def convert_016_017(data): def convert_017_018(data): + data["server_conn"]["ip_address"] = data["server_conn"].pop("peer_address") data["version"] = (0, 18) return data diff --git a/mitmproxy/models/__init__.py b/mitmproxy/models/__init__.py index 653b19fd..f5f0213a 100644 --- a/mitmproxy/models/__init__.py +++ b/mitmproxy/models/__init__.py @@ -1,9 +1,10 @@ from __future__ import (absolute_import, print_function, division) from .http import ( - HTTPFlow, HTTPRequest, HTTPResponse, Headers, decoded, + HTTPFlow, HTTPRequest, HTTPResponse, Headers, make_error_response, make_connect_request, make_connect_response, expect_continue_response ) +from netlib.http import decoded from .connections import ClientConnection, ServerConnection from .flow import Flow, Error diff --git a/mitmproxy/models/connections.py b/mitmproxy/models/connections.py index 14545842..91590bca 100644 --- a/mitmproxy/models/connections.py +++ b/mitmproxy/models/connections.py @@ -10,6 +10,17 @@ from .. import stateobject, utils class ClientConnection(tcp.BaseHandler, stateobject.StateObject): + """ + A client connection + + Attributes: + address: Remote address + ssl_established: True if TLS is established, False otherwise + clientcert: The TLS client certificate + timestamp_start: Connection start timestamp + timestamp_ssl_setup: TLS established timestamp + timestamp_end: Connection end timestamp + """ def __init__(self, client_connection, address, server): # Eventually, this object is restored from state. We don't have a # connection then. @@ -47,11 +58,11 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject): _stateobject_attributes = dict( address=tcp.Address, - clientcert=certutils.SSLCert, ssl_established=bool, + clientcert=certutils.SSLCert, timestamp_start=float, + timestamp_ssl_setup=float, timestamp_end=float, - timestamp_ssl_setup=float ) def copy(self): @@ -90,6 +101,22 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject): class ServerConnection(tcp.TCPClient, stateobject.StateObject): + """ + A server connection + + Attributes: + address: Remote address. Can be both a domain or an IP address. + ip_address: Resolved remote IP address. + source_address: Local IP address + ssl_established: True if TLS is established, False otherwise + cert: The certificate presented by the remote during the TLS handshake + sni: Server Name Indication sent by the proxy during the TLS handshake + via: The underlying server connection (e.g. the connection to the upstream proxy in upstream proxy mode) + timestamp_start: Connection start timestamp + timestamp_tcp_setup: TCP ACK received timestamp + timestamp_ssl_setup: TLS established timestamp + timestamp_end: Connection end timestamp + """ def __init__(self, address, source_address=None): tcp.TCPClient.__init__(self, address, source_address) @@ -123,16 +150,16 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject): return self.ssl_established _stateobject_attributes = dict( - timestamp_start=float, - timestamp_end=float, - timestamp_tcp_setup=float, - timestamp_ssl_setup=float, address=tcp.Address, - peer_address=tcp.Address, + ip_address=tcp.Address, source_address=tcp.Address, - cert=certutils.SSLCert, ssl_established=bool, - sni=str + cert=certutils.SSLCert, + sni=str, + timestamp_start=float, + timestamp_tcp_setup=float, + timestamp_ssl_setup=float, + timestamp_end=float, ) @classmethod @@ -145,6 +172,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject): def make_dummy(cls, address): return cls.from_state(dict( address=dict(address=address, use_ipv6=False), + ip_address=dict(address=address, use_ipv6=False), cert=None, sni=None, source_address=dict(address=('', 0), use_ipv6=False), diff --git a/mitmproxy/models/http.py b/mitmproxy/models/http.py index 08fc5e46..77a809cf 100644 --- a/mitmproxy/models/http.py +++ b/mitmproxy/models/http.py @@ -1,14 +1,9 @@ from __future__ import (absolute_import, print_function, division) import cgi -import copy -import warnings -from email.utils import parsedate_tz, formatdate, mktime_tz -import time from netlib import encoding -from netlib.http import status_codes, Headers, Request, Response, decoded +from netlib.http import status_codes, Headers, Request, Response from netlib.tcp import Address -from .. import utils from .. import version from .flow import Flow @@ -30,41 +25,9 @@ class MessageMixin(object): class HTTPRequest(MessageMixin, Request): """ - An HTTP request. - - Exposes the following attributes: - - method: HTTP method - - scheme: URL scheme (http/https) - - host: Target hostname of the request. This is not neccessarily the - directy upstream server (which could be another proxy), but it's always - the target server we want to reach at the end. This attribute is either - inferred from the request itself (absolute-form, authority-form) or from - the connection metadata (e.g. the host in reverse proxy mode). - - port: Destination port - - path: Path portion of the URL (not present in authority-form) - - http_version: HTTP version, e.g. "HTTP/1.1" - - headers: Headers object - - content: Content of the request, the value is None if there is content - associated, but not present. - - first_line_format: The request form. The following values are possible: - - - relative (GET /index.html, OPTIONS *) (origin form or asterisk form) - - absolute (GET http://example.com:80/index.html) - - authority-form (CONNECT example.com:443) - Details: http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-25#section-5.3 - - timestamp_start: Timestamp indicating when request transmission started - - timestamp_end: Timestamp indicating when request transmission ended + A mitmproxy HTTP request. + This is a very thin wrapper on top of :py:class:`netlib.http.Request` and + may be removed in the future. """ def __init__( @@ -123,6 +86,9 @@ class HTTPRequest(MessageMixin, Request): @classmethod def wrap(self, request): + """ + Wraps an existing :py:class:`netlib.http.Request`. + """ req = HTTPRequest( first_line_format=request.data.first_line_format, method=request.data.method, @@ -141,27 +107,12 @@ class HTTPRequest(MessageMixin, Request): def __hash__(self): return id(self) -class HTTPResponse(MessageMixin, Response): +class HTTPResponse(MessageMixin, Response): """ - An HTTP response. - - Exposes the following attributes: - - http_version: HTTP version, e.g. "HTTP/1.1" - - status_code: HTTP response status code - - msg: HTTP response message - - headers: Headers object - - content: Content of the response, the value is None if there is content - associated, but not present. - - timestamp_start: Timestamp indicating when request transmission started - - timestamp_end: Timestamp indicating when request transmission ended + A mitmproxy HTTP response. + This is a very thin wrapper on top of :py:class:`netlib.http.Response` and + may be removed in the future. """ def __init__( @@ -192,6 +143,9 @@ class HTTPResponse(MessageMixin, Response): @classmethod def wrap(self, response): + """ + Wraps an existing :py:class:`netlib.http.Response`. + """ resp = HTTPResponse( http_version=response.data.http_version, status_code=response.data.status_code, @@ -211,11 +165,11 @@ class HTTPFlow(Flow): transaction. Attributes: - request: HTTPRequest object - response: HTTPResponse object - error: Error object - server_conn: ServerConnection object - client_conn: ClientConnection object + request: :py:class:`HTTPRequest` object + response: :py:class:`HTTPResponse` object + error: :py:class:`Error` object + server_conn: :py:class:`ServerConnection` object + client_conn: :py:class:`ClientConnection` object intercepted: Is this flow currently being intercepted? live: Does this flow have a live client connection? diff --git a/netlib/http/request.py b/netlib/http/request.py index 692ba30f..160ce3a3 100644 --- a/netlib/http/request.py +++ b/netlib/http/request.py @@ -18,6 +18,7 @@ from .message import Message, _native, _always_bytes, MessageData # https://bugzilla.mozilla.org/show_bug.cgi?id=45891 host_header_re = re.compile(r"^(?P<host>[^:]+|\[.+\])(?::(?P<port>\d+))?$") + class RequestData(MessageData): def __init__(self, first_line_format, method, scheme, host, port, path, http_version, headers=None, content=None, timestamp_start=None, timestamp_end=None): diff --git a/netlib/tcp.py b/netlib/tcp.py index 04aa868b..d26bb5f7 100644 --- a/netlib/tcp.py +++ b/netlib/tcp.py @@ -458,11 +458,11 @@ class _Connection(object): def __init__(self, connection): if connection: self.connection = connection - self.peer_address = Address(connection.getpeername()) + self.ip_address = Address(connection.getpeername()) self._makefile() else: self.connection = None - self.peer_address = None + self.ip_address = None self.rfile = None self.wfile = None @@ -708,7 +708,7 @@ class TCPClient(_Connection): 'Error connecting to "%s": %s' % (self.address.host, err)) self.connection = connection - self.peer_address = Address(connection.getpeername()) + self.ip_address = Address(connection.getpeername()) self._makefile() def settimeout(self, n): diff --git a/test/mitmproxy/tutils.py b/test/mitmproxy/tutils.py index 191eb966..d51ac185 100644 --- a/test/mitmproxy/tutils.py +++ b/test/mitmproxy/tutils.py @@ -100,7 +100,7 @@ def tserver_conn(): c = ServerConnection.from_state(dict( address=dict(address=("address", 22), use_ipv6=True), source_address=dict(address=("address", 22), use_ipv6=True), - peer_address=None, + ip_address=None, cert=None, timestamp_start=1, timestamp_tcp_setup=2, |