aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy')
-rw-r--r--libmproxy/console/flowview.py14
-rw-r--r--libmproxy/protocol/http.py89
-rw-r--r--libmproxy/protocol/primitives.py21
-rw-r--r--libmproxy/proxy/__init__.py3
-rw-r--r--libmproxy/proxy/config.py6
-rw-r--r--libmproxy/proxy/server.py1
6 files changed, 70 insertions, 64 deletions
diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py
index 9063c3e1..014d44c0 100644
--- a/libmproxy/console/flowview.py
+++ b/libmproxy/console/flowview.py
@@ -554,17 +554,17 @@ class FlowView(common.WWrap):
conn.headers = flow.ODictCaseless(lst)
def set_query(self, lst, conn):
- conn.query = flow.ODict(lst)
+ conn.set_query(flow.ODict(lst))
def set_path_components(self, lst, conn):
- conn.path_components = [i[0] for i in lst]
+ conn.set_path_components([i[0] for i in lst])
def set_form(self, lst, conn):
- conn.form_urlencoded = flow.ODict(lst)
+ conn.set_form_urlencoded(flow.ODict(lst))
def edit_form(self, conn):
self.master.view_grideditor(
- grideditor.URLEncodedFormEditor(self.master, conn.form_urlencoded.lst, self.set_form, conn)
+ grideditor.URLEncodedFormEditor(self.master, conn.get_form_urlencoded().lst, self.set_form, conn)
)
def edit_form_confirm(self, key, conn):
@@ -589,7 +589,7 @@ class FlowView(common.WWrap):
c = self.master.spawn_editor(conn.content or "")
conn.content = c.rstrip("\n") # what?
elif part == "f":
- if not conn.form_urlencoded and conn.content:
+ if not conn.get_form_urlencoded() and conn.content:
self.master.prompt_onekey(
"Existing body is not a URL-encoded form. Clear and edit?",
[
@@ -604,11 +604,11 @@ class FlowView(common.WWrap):
elif part == "h":
self.master.view_grideditor(grideditor.HeaderEditor(self.master, conn.headers.lst, self.set_headers, conn))
elif part == "p":
- p = conn.path_components
+ p = conn.get_path_components()
p = [[i] for i in p]
self.master.view_grideditor(grideditor.PathEditor(self.master, p, self.set_path_components, conn))
elif part == "q":
- self.master.view_grideditor(grideditor.QueryEditor(self.master, conn.query.lst, self.set_query, conn))
+ self.master.view_grideditor(grideditor.QueryEditor(self.master, conn.get_query().lst, self.set_query, conn))
elif part == "u" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
self.master.prompt_edit("URL", conn.url, self.set_url)
elif part == "m" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py
index 90ee127c..9593c3cb 100644
--- a/libmproxy/protocol/http.py
+++ b/libmproxy/protocol/http.py
@@ -29,13 +29,13 @@ def get_line(fp):
def send_connect_request(conn, host, port, update_state=True):
upstream_request = HTTPRequest("authority", "CONNECT", None, host, port, None,
(1, 1), ODictCaseless(), "")
- conn.send(upstream_request._assemble())
+ conn.send(upstream_request.assemble())
resp = HTTPResponse.from_stream(conn.rfile, upstream_request.method)
if resp.code != 200:
raise proxy.ProxyError(resp.code,
"Cannot establish SSL " +
"connection with upstream proxy: \r\n" +
- str(resp._assemble()))
+ str(resp.assemble()))
if update_state:
conn.state.append(("http", {
"state": "connect",
@@ -73,6 +73,9 @@ class decoded(object):
class HTTPMessage(stateobject.SimpleStateObject):
+ """
+ Base class for HTTPRequest and HTTPResponse
+ """
def __init__(self, httpversion, headers, content, timestamp_start=None,
timestamp_end=None):
self.httpversion = httpversion
@@ -162,31 +165,31 @@ class HTTPMessage(stateobject.SimpleStateObject):
"""
Parse an HTTP message from a file stream
"""
- raise NotImplementedError # pragma: nocover
+ raise NotImplementedError() # pragma: nocover
def _assemble_first_line(self):
"""
Returns the assembled request/response line
"""
- raise NotImplementedError # pragma: nocover
+ raise NotImplementedError() # pragma: nocover
def _assemble_headers(self):
"""
Returns the assembled headers
"""
- raise NotImplementedError # pragma: nocover
+ raise NotImplementedError() # pragma: nocover
def _assemble_head(self):
"""
Returns the assembled request/response line plus headers
"""
- raise NotImplementedError # pragma: nocover
+ raise NotImplementedError() # pragma: nocover
- def _assemble(self):
+ def assemble(self):
"""
Returns the assembled request/response
"""
- raise NotImplementedError # pragma: nocover
+ raise NotImplementedError() # pragma: nocover
class HTTPRequest(HTTPMessage):
@@ -195,7 +198,17 @@ class HTTPRequest(HTTPMessage):
Exposes the following attributes:
- flow: Flow object the request belongs to
+ method: HTTP method
+
+ scheme: URL scheme (http/https) (absolute-form only)
+
+ host: Host portion of the URL (absolute-form and authority-form only)
+
+ port: Destination port (absolute-form and authority-form only)
+
+ path: Path portion of the URL (not present in authority-form)
+
+ httpversion: HTTP version tuple, e.g. (1,1)
headers: ODictCaseless object
@@ -211,18 +224,6 @@ class HTTPRequest(HTTPMessage):
form_out: The request form which mitmproxy has send out to the destination
- method: HTTP method
-
- scheme: URL scheme (http/https) (absolute-form only)
-
- host: Host portion of the URL (absolute-form and authority-form only)
-
- port: Destination port (absolute-form and authority-form only)
-
- path: Path portion of the URL (not present in authority-form)
-
- httpversion: HTTP version tuple
-
timestamp_start: Timestamp indicating when request transmission started
timestamp_end: Timestamp indicating when request transmission ended
@@ -364,7 +365,7 @@ class HTTPRequest(HTTPMessage):
def _assemble_head(self, form=None):
return "%s\r\n%s\r\n" % (self._assemble_first_line(form), self._assemble_headers())
- def _assemble(self, form=None):
+ def assemble(self, form=None):
"""
Assembles the request for transmission to the server. We make some
modifications to make sure interception works properly.
@@ -417,8 +418,7 @@ class HTTPRequest(HTTPMessage):
"""
self.headers["Host"] = [self.host]
- @property
- def form_urlencoded(self):
+ def get_form_urlencoded(self):
"""
Retrieves the URL-encoded form data, returning an ODict object.
Returns an empty ODict if there is no data or the content-type
@@ -428,8 +428,7 @@ class HTTPRequest(HTTPMessage):
return ODict(utils.urldecode(self.content))
return ODict([])
- @form_urlencoded.setter
- def form_urlencoded(self, odict):
+ def set_form_urlencoded(self, odict):
"""
Sets the body to the URL-encoded form data, and adds the
appropriate content-type header. Note that this will destory the
@@ -440,8 +439,7 @@ class HTTPRequest(HTTPMessage):
self.headers["Content-Type"] = [HDR_FORM_URLENCODED]
self.content = utils.urlencode(odict.lst)
- @property
- def path_components(self):
+ def get_path_components(self):
"""
Returns the path components of the URL as a list of strings.
@@ -450,8 +448,7 @@ class HTTPRequest(HTTPMessage):
_, _, path, _, _, _ = urlparse.urlparse(self.url)
return [urllib.unquote(i) for i in path.split("/") if i]
- @path_components.setter
- def path_components(self, lst):
+ def set_path_components(self, lst):
"""
Takes a list of strings, and sets the path component of the URL.
@@ -462,8 +459,7 @@ class HTTPRequest(HTTPMessage):
scheme, netloc, _, params, query, fragment = urlparse.urlparse(self.url)
self.url = urlparse.urlunparse([scheme, netloc, path, params, query, fragment])
- @property
- def query(self):
+ def get_query(self):
"""
Gets the request query string. Returns an ODict object.
"""
@@ -472,8 +468,7 @@ class HTTPRequest(HTTPMessage):
return ODict(utils.urldecode(query))
return ODict([])
- @query.setter
- def query(self, odict):
+ def set_query(self, odict):
"""
Takes an ODict object, and sets the request query string.
"""
@@ -528,8 +523,7 @@ class HTTPRequest(HTTPMessage):
raise ValueError("Invalid URL: %s" % url)
self.scheme, self.host, self.port, self.path = parts
- @property
- def cookies(self):
+ def get_cookies(self):
cookie_headers = self.headers.get("cookie")
if not cookie_headers:
return None
@@ -560,7 +554,7 @@ class HTTPResponse(HTTPMessage):
Exposes the following attributes:
- flow: Flow object the request belongs to
+ httpversion: HTTP version tuple, e.g. (1,1)
code: HTTP response code
@@ -572,8 +566,6 @@ class HTTPResponse(HTTPMessage):
is content associated, but not present. CONTENT_MISSING evaluates
to False to make checking for the presence of content natural.
- httpversion: HTTP version tuple
-
timestamp_start: Timestamp indicating when request transmission started
timestamp_end: Timestamp indicating when request transmission ended
@@ -661,7 +653,7 @@ class HTTPResponse(HTTPMessage):
return '%s\r\n%s\r\n' % (
self._assemble_first_line(), self._assemble_headers(preserve_transfer_encoding=preserve_transfer_encoding))
- def _assemble(self):
+ def assemble(self):
"""
Assembles the response for transmission to the client. We make some
modifications to make sure interception works properly.
@@ -726,8 +718,7 @@ class HTTPResponse(HTTPMessage):
if c:
self.headers["set-cookie"] = c
- @property
- def cookies(self):
+ def get_cookies(self):
cookie_headers = self.headers.get("set-cookie")
if not cookie_headers:
return None
@@ -745,12 +736,14 @@ class HTTPResponse(HTTPMessage):
class HTTPFlow(Flow):
"""
- A Flow is a collection of objects representing a single HTTP
+ A HTTPFlow is a collection of objects representing a single HTTP
transaction. The main attributes are:
request: HTTPRequest object
response: HTTPResponse object
error: Error object
+ server_conn: ServerConnection object
+ client_conn: ClientConnection object
Note that it's possible for a Flow to have both a response and an error
object. This might happen, for instance, when a response was received
@@ -866,6 +859,10 @@ class HttpAuthenticationError(Exception):
class HTTPHandler(ProtocolHandler):
+ """
+ HTTPHandler implements mitmproxys understanding of the HTTP protocol.
+
+ """
def __init__(self, c):
super(HTTPHandler, self).__init__(c)
self.expected_form_in = c.config.http_form_in
@@ -878,7 +875,7 @@ class HTTPHandler(ProtocolHandler):
def get_response_from_server(self, request, include_body=True):
self.c.establish_server_connection()
- request_raw = request._assemble()
+ request_raw = request.assemble()
for i in range(2):
try:
@@ -957,7 +954,7 @@ class HTTPHandler(ProtocolHandler):
if not flow.response.stream:
# no streaming:
# we already received the full response from the server and can send it to the client straight away.
- self.c.client_conn.send(flow.response._assemble())
+ self.c.client_conn.send(flow.response.assemble())
else:
# streaming:
# First send the body and then transfer the response incrementally:
@@ -1225,7 +1222,7 @@ class RequestReplayThread(threading.Thread):
server.establish_ssl(self.config.clientcerts, sni=r.host)
r.form_out = "relative"
- server.send(r._assemble())
+ server.send(r.assemble())
self.flow.response = HTTPResponse.from_stream(server.rfile, r.method,
body_size_limit=self.config.body_size_limit)
self.channel.ask("response", self.flow)
diff --git a/libmproxy/protocol/primitives.py b/libmproxy/protocol/primitives.py
index ee1199fc..ecad9d9e 100644
--- a/libmproxy/protocol/primitives.py
+++ b/libmproxy/protocol/primitives.py
@@ -12,9 +12,9 @@ class Error(stateobject.SimpleStateObject):
"""
An Error.
- This is distinct from an HTTP error response (say, a code 500), which
- is represented by a normal Response object. This class is responsible
- for indicating errors that fall outside of normal HTTP communications,
+ This is distinct from an protocol error response (say, a HTTP code 500), which
+ is represented by a normal HTTPResponse object. This class is responsible
+ for indicating errors that fall outside of normal protocol communications,
like interrupted connections, timeouts, protocol errors.
Exposes the following attributes:
@@ -52,6 +52,10 @@ class Error(stateobject.SimpleStateObject):
class Flow(stateobject.SimpleStateObject):
+ """
+ A Flow is a collection of objects representing a single transaction.
+ This class is usually subclassed for each protocol, e.g. HTTPFlow.
+ """
def __init__(self, conntype, client_conn, server_conn, live=None):
self.conntype = conntype
self.client_conn = client_conn
@@ -117,6 +121,10 @@ class Flow(stateobject.SimpleStateObject):
class ProtocolHandler(object):
+ """
+ A ProtocolHandler implements an application-layer protocol, e.g. HTTP.
+ See: libmproxy.protocol.http.HTTPHandler
+ """
def __init__(self, c):
self.c = c
"""@type: libmproxy.proxy.server.ConnectionHandler"""
@@ -148,13 +156,14 @@ class ProtocolHandler(object):
class LiveConnection(object):
"""
- This facade allows protocol handlers to interface with a live connection,
- without requiring the expose the ConnectionHandler.
+ This facade allows interested parties (FlowMaster, inline scripts) to interface with a live connection,
+ without requiring to expose the internals of the ConnectionHandler.
"""
def __init__(self, c):
self.c = c
- self._backup_server_conn = None
"""@type: libmproxy.proxy.server.ConnectionHandler"""
+ self._backup_server_conn = None
+ """@type: libmproxy.proxy.connection.ServerConnection"""
def change_server(self, address, ssl=False, force=False, persistent_change=False):
address = netlib.tcp.Address.wrap(address)
diff --git a/libmproxy/proxy/__init__.py b/libmproxy/proxy/__init__.py
index f5d6a2d0..e4c20030 100644
--- a/libmproxy/proxy/__init__.py
+++ b/libmproxy/proxy/__init__.py
@@ -1 +1,2 @@
-from .primitives import * \ No newline at end of file
+from .primitives import *
+from .config import ProxyConfig
diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py
index 6d4c078b..ea815c69 100644
--- a/libmproxy/proxy/config.py
+++ b/libmproxy/proxy/config.py
@@ -1,8 +1,8 @@
from __future__ import absolute_import
import os
-from .. import utils, platform
import re
from netlib import http_auth, certutils
+from .. import utils, platform
from .primitives import ConstUpstreamServerResolver, TransparentUpstreamServerResolver
TRANSPARENT_SSL_PORTS = [443, 8443]
@@ -11,7 +11,7 @@ CONF_DIR = "~/.mitmproxy"
class ProxyConfig:
- def __init__(self, confdir=CONF_DIR, clientcerts=None,
+ def __init__(self, confdir=CONF_DIR, ca_file=None, clientcerts=None,
no_upstream_cert=False, body_size_limit=None,
mode=None, upstream_server=None, http_form_in=None, http_form_out=None,
authenticator=None, ignore=[],
@@ -44,7 +44,7 @@ class ProxyConfig:
self.ignore = [re.compile(i, re.IGNORECASE) for i in ignore]
self.authenticator = authenticator
self.confdir = os.path.expanduser(confdir)
- self.ca_file = os.path.join(self.confdir, CONF_BASENAME + "-ca.pem")
+ self.ca_file = ca_file or os.path.join(self.confdir, CONF_BASENAME + "-ca.pem")
self.certstore = certutils.CertStore.from_store(self.confdir, CONF_BASENAME)
for spec, cert in certs:
self.certstore.add_cert_file(spec, cert)
diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py
index 58e386ab..aacc908e 100644
--- a/libmproxy/proxy/server.py
+++ b/libmproxy/proxy/server.py
@@ -1,5 +1,4 @@
from __future__ import absolute_import
-import re
import socket
from OpenSSL import SSL