aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-09-05 15:16:20 +0200
committerMaximilian Hils <git@maximilianhils.com>2014-09-05 15:16:20 +0200
commit2a6337343a14f7f72c28d8bf5f24220f6d9ca6d0 (patch)
tree755d8cf7f6578c9346e9f3c68604615177cf9332
parentf2570c773aa18e4ac236b1cf7f43acfb4ca080dd (diff)
downloadmitmproxy-2a6337343a14f7f72c28d8bf5f24220f6d9ca6d0.tar.gz
mitmproxy-2a6337343a14f7f72c28d8bf5f24220f6d9ca6d0.tar.bz2
mitmproxy-2a6337343a14f7f72c28d8bf5f24220f6d9ca6d0.zip
update docs, mostly revert 2f44b26b4cd014e03dd62a125d79af9b81663a93
-rw-r--r--doc-src/scripting/inlinescripts.html53
-rw-r--r--examples/add_header.py4
-rw-r--r--examples/dup_and_replay.py6
-rwxr-xr-xexamples/flowbasic9
-rw-r--r--examples/modify_form.py10
-rw-r--r--examples/modify_querystring.py7
-rw-r--r--examples/nonblocking.py3
-rw-r--r--examples/redirect_requests.py4
-rwxr-xr-xexamples/stickycookies8
-rw-r--r--examples/stub.py6
-rw-r--r--examples/upsidedownternet.py2
-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
-rw-r--r--test/test_flow.py86
-rw-r--r--test/test_protocol_http.py8
-rw-r--r--test/test_proxy.py2
20 files changed, 176 insertions, 166 deletions
diff --git a/doc-src/scripting/inlinescripts.html b/doc-src/scripting/inlinescripts.html
index 738f8dc3..eef4e440 100644
--- a/doc-src/scripting/inlinescripts.html
+++ b/doc-src/scripting/inlinescripts.html
@@ -29,46 +29,45 @@ The new header will be added to all responses passing through the proxy.
Called once on startup, before any other events.
-### clientconnect(ScriptContext, ClientConnect)
+### clientconnect(ScriptContext, ConnectionHandler)
Called when a client initiates a connection to the proxy. Note that
a connection can correspond to multiple HTTP requests.
-
-### serverconnect(ScriptContext, ServerConnection)
+### serverconnect(ScriptContext, ConnectionHandler)
Called when the proxy initiates a connection to the target server. Note that
a connection can correspond to multiple HTTP requests.
-### request(ScriptContext, Flow)
+### request(ScriptContext, HTTPFlow)
-Called when a client request has been received. The __Flow__ object is
+Called when a client request has been received. The __HTTPFlow__ object is
guaranteed to have a non-None __request__ attribute.
-### responseheaders(ScriptContext, Flow)
+### responseheaders(ScriptContext, HTTPFlow)
Called when the headers of a server response have been received.
This will always be called before the response hook.
-The __Flow__ object is guaranteed to have non-None __request__ and
-__response__ attributes. __response.content__ will not be valid,
+The __HTTPFlow__ object is guaranteed to have non-None __request__ and
+__response__ attributes. __response.content__ will be None,
as the response body has not been read yet.
-### response(ScriptContext, Flow)
+### response(ScriptContext, HTTPFlow)
-Called when a server response has been received. The __Flow__ object is
+Called when a server response has been received. The __HTTPFlow__ object is
guaranteed to have non-None __request__ and __response__ attributes.
Note that if response streaming is enabled for this response,
__response.content__ will not contain the response body.
-### error(ScriptContext, Flow)
+### error(ScriptContext, HTTPFlow)
Called when a flow error has occurred, e.g. invalid server responses, or
interrupted connections. This is distinct from a valid server HTTP error
-response, which is simply a response with an HTTP error code. The __Flow__
+response, which is simply a response with an HTTP error code. The __HTTPFlow__
object is guaranteed to have non-None __request__ and __error__ attributes.
-### clientdisconnect(ScriptContext, ClientDisconnect)
+### clientdisconnect(ScriptContext, ConnectionHandler)
Called when a client disconnects from the proxy.
@@ -97,22 +96,10 @@ The main classes you will deal with in writing mitmproxy scripts are:
<td>Describes a server connection.</td>
</tr>
<tr>
- <th>libmproxy.protocol.primitives.Error</th>
- <td>A communications error.</td>
- </tr>
- <tr>
<th>libmproxy.protocol.http.HTTPFlow</th>
<td>A collection of objects representing a single HTTP transaction.</td>
</tr>
<tr>
- <th>libmproxy.flow.ODict</th>
-
- <td>A dictionary-like object for managing sets of key/value data. There
- is also a variant called CaselessODict that ignores key case for some
- calls (used mainly for headers).
- </td>
- </tr>
- <tr>
<th>libmproxy.protocol.http.HTTPResponse</th>
<td>An HTTP response.</td>
</tr>
@@ -121,10 +108,22 @@ The main classes you will deal with in writing mitmproxy scripts are:
<td>An HTTP request.</td>
</tr>
<tr>
+ <th>libmproxy.protocol.primitives.Error</th>
+ <td>A communications error.</td>
+ </tr>
+ <tr>
<th>libmproxy.script.ScriptContext</th>
<td> A handle for interacting with mitmproxy's from within scripts.</td>
</tr>
<tr>
+ <th>libmproxy.flow.ODict</th>
+
+ <td>A dictionary-like object for managing sets of key/value data. There
+ is also a variant called CaselessODict that ignores key case for some
+ calls (used mainly for headers).
+ </td>
+ </tr>
+ <tr>
<th>libmproxy.certutils.SSLCert</th>
<td>Exposes information SSL certificates.</td>
</tr>
@@ -161,9 +160,9 @@ flows from a file (see the "scripted data transformation" example on the
one-shot script on a single flow through the _|_ (pipe) shortcut in mitmproxy.
In this case, there are no client connections, and the events are run in the
-following order: __start__, __request__, __response__, __error__, __done__. If
+following order: __start__, __request__, __responseheaders__, __response__, __error__, __done__. If
the flow doesn't have a __response__ or __error__ associated with it, the
-matching event will be skipped.
+matching events will be skipped.
## Spaces in the script path
By default, spaces are interpreted as separator between the inline script and its arguments (e.g. <code>-s "foo.py
diff --git a/examples/add_header.py b/examples/add_header.py
index 0c0593d1..b9c8c1c6 100644
--- a/examples/add_header.py
+++ b/examples/add_header.py
@@ -1,2 +1,2 @@
-def response(context, flow):
- flow.response.headers["newheader"] = ["foo"]
+def response(ctx, flow):
+ flow.response.headers["newheader"] = ["foo"] \ No newline at end of file
diff --git a/examples/dup_and_replay.py b/examples/dup_and_replay.py
index 9c58d3a4..b38c2b7e 100644
--- a/examples/dup_and_replay.py
+++ b/examples/dup_and_replay.py
@@ -1,4 +1,4 @@
def request(ctx, flow):
- f = ctx.duplicate_flow(flow)
- f.request.path = "/changed"
- ctx.replay_request(f)
+ f = ctx.duplicate_flow(flow)
+ f.request.path = "/changed"
+ ctx.replay_request(f) \ No newline at end of file
diff --git a/examples/flowbasic b/examples/flowbasic
index 8dbe2f28..2b44be3f 100755
--- a/examples/flowbasic
+++ b/examples/flowbasic
@@ -3,11 +3,14 @@
This example shows how to build a proxy based on mitmproxy's Flow
primitives.
+ Heads Up: In the majority of cases, you want to use inline scripts.
+
Note that request and response messages are not automatically replied to,
so we need to implement handlers to do this.
"""
import os
-from libmproxy import proxy, flow
+from libmproxy import flow, proxy
+from libmproxy.proxy.server import ProxyServer
class MyMaster(flow.FlowMaster):
def run(self):
@@ -31,9 +34,9 @@ class MyMaster(flow.FlowMaster):
config = proxy.ProxyConfig(
- cacert = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
+ ca_file = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
)
state = flow.State()
-server = proxy.ProxyServer(config, 8080)
+server = ProxyServer(config, 8080)
m = MyMaster(server, state)
m.run()
diff --git a/examples/modify_form.py b/examples/modify_form.py
index cb12ee0f..6d651b19 100644
--- a/examples/modify_form.py
+++ b/examples/modify_form.py
@@ -1,8 +1,6 @@
-def request(context, flow):
+def request(ctx, flow):
if "application/x-www-form-urlencoded" in flow.request.headers["content-type"]:
- frm = flow.request.form_urlencoded
- frm["mitmproxy"] = ["rocks"]
- flow.request.form_urlencoded = frm
-
-
+ form = flow.request.get_form_urlencoded()
+ form["mitmproxy"] = ["rocks"]
+ flow.request.set_form_urlencoded(form) \ No newline at end of file
diff --git a/examples/modify_querystring.py b/examples/modify_querystring.py
index 7e3a068a..56fbbb32 100644
--- a/examples/modify_querystring.py
+++ b/examples/modify_querystring.py
@@ -1,7 +1,6 @@
-def request(context, flow):
- q = flow.request.query
+def request(ctx, flow):
+ q = flow.request.get_query()
if q:
q["mitmproxy"] = ["rocks"]
- flow.request.query = q
-
+ flow.request.set_query(q) \ No newline at end of file
diff --git a/examples/nonblocking.py b/examples/nonblocking.py
index 9a131b32..1396742a 100644
--- a/examples/nonblocking.py
+++ b/examples/nonblocking.py
@@ -1,8 +1,9 @@
import time
from libmproxy.script import concurrent
+
@concurrent
def request(context, flow):
print "handle request: %s%s" % (flow.request.host, flow.request.path)
time.sleep(5)
- print "start request: %s%s" % (flow.request.host, flow.request.path)
+ print "start request: %s%s" % (flow.request.host, flow.request.path) \ No newline at end of file
diff --git a/examples/redirect_requests.py b/examples/redirect_requests.py
index cc642039..c5561839 100644
--- a/examples/redirect_requests.py
+++ b/examples/redirect_requests.py
@@ -6,7 +6,9 @@ This example shows two ways to redirect flows to other destinations.
"""
-def request(context, flow):
+def request(ctx, flow):
+ # pretty_host(hostheader=True) takes the Host: header of the request into account,
+ # which is useful in transparent mode where we usually only have the IP otherwise.
if flow.request.pretty_host(hostheader=True).endswith("example.com"):
resp = HTTPResponse(
[1, 1], 200, "OK",
diff --git a/examples/stickycookies b/examples/stickycookies
index 17cd6019..2aab31d6 100755
--- a/examples/stickycookies
+++ b/examples/stickycookies
@@ -5,8 +5,10 @@ implement functionality similar to the "sticky cookies" option. This is at
a lower level than the Flow mechanism, so we're dealing directly with
request and response objects.
"""
-from libmproxy import controller, proxy
import os
+from libmproxy import controller, proxy
+from libmproxy.proxy.server import ProxyServer
+
class StickyMaster(controller.Master):
def __init__(self, server):
@@ -35,8 +37,8 @@ class StickyMaster(controller.Master):
config = proxy.ProxyConfig(
- cacert = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
+ ca_file = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
)
-server = proxy.ProxyServer(config, 8080)
+server = ProxyServer(config, 8080)
m = StickyMaster(server)
m.run()
diff --git a/examples/stub.py b/examples/stub.py
index 0cf67db7..5976dd76 100644
--- a/examples/stub.py
+++ b/examples/stub.py
@@ -7,14 +7,14 @@ def start(ctx, argv):
"""
ctx.log("start")
-def clientconnect(ctx, client_connect):
+def clientconnect(ctx, conn_handler):
"""
Called when a client initiates a connection to the proxy. Note that a
connection can correspond to multiple HTTP requests
"""
ctx.log("clientconnect")
-def serverconnect(ctx, server_connection):
+def serverconnect(ctx, conn_handler):
"""
Called when the proxy initiates a connection to the target server. Note that a
connection can correspond to multiple HTTP requests
@@ -50,7 +50,7 @@ def error(ctx, flow):
"""
ctx.log("error")
-def clientdisconnect(ctx, client_disconnect):
+def clientdisconnect(ctx, conn_handler):
"""
Called when a client disconnects from the proxy.
"""
diff --git a/examples/upsidedownternet.py b/examples/upsidedownternet.py
index 181a40c2..a52b6d30 100644
--- a/examples/upsidedownternet.py
+++ b/examples/upsidedownternet.py
@@ -1,7 +1,7 @@
import cStringIO
from PIL import Image
-def response(context, flow):
+def response(ctx, flow):
if flow.response.headers["content-type"] == ["image/png"]:
s = cStringIO.StringIO(flow.response.content)
img = Image.open(s).rotate(180)
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
diff --git a/test/test_flow.py b/test/test_flow.py
index 4bc2391e..914138c9 100644
--- a/test/test_flow.py
+++ b/test/test_flow.py
@@ -481,7 +481,7 @@ class TestSerialize:
f2 = l[0]
assert f2._get_state() == f._get_state()
- assert f2.request._assemble() == f.request._assemble()
+ assert f2.request.assemble() == f.request.assemble()
def test_load_flows(self):
r = self._treader()
@@ -757,18 +757,18 @@ class TestRequest:
r.url = u
tutils.raises(ValueError, setattr, r, "url", "")
assert r.url == u
- assert r._assemble()
- assert r.size() == len(r._assemble())
+ assert r.assemble()
+ assert r.size() == len(r.assemble())
r2 = r.copy()
assert r == r2
r.content = None
- assert r._assemble()
- assert r.size() == len(r._assemble())
+ assert r.assemble()
+ assert r.size() == len(r.assemble())
r.content = CONTENT_MISSING
- tutils.raises("Cannot assemble flow with CONTENT_MISSING", r._assemble)
+ tutils.raises("Cannot assemble flow with CONTENT_MISSING", r.assemble)
def test_get_url(self):
r = tutils.treq()
@@ -794,58 +794,58 @@ class TestRequest:
def test_path_components(self):
r = tutils.treq()
r.path = "/"
- assert r.path_components == []
+ assert r.get_path_components() == []
r.path = "/foo/bar"
- assert r.path_components == ["foo", "bar"]
+ assert r.get_path_components() == ["foo", "bar"]
q = flow.ODict()
q["test"] = ["123"]
- r.query = q
- assert r.path_components == ["foo", "bar"]
-
- r.path_components = []
- assert r.path_components == []
- r.path_components = ["foo"]
- assert r.path_components == ["foo"]
- r.path_components = ["/oo"]
- assert r.path_components == ["/oo"]
+ r.set_query(q)
+ assert r.get_path_components() == ["foo", "bar"]
+
+ r.set_path_components([])
+ assert r.get_path_components() == []
+ r.set_path_components(["foo"])
+ assert r.get_path_components() == ["foo"]
+ r.set_path_components(["/oo"])
+ assert r.get_path_components() == ["/oo"]
assert "%2F" in r.path
def test_getset_form_urlencoded(self):
d = flow.ODict([("one", "two"), ("three", "four")])
r = tutils.treq(content=utils.urlencode(d.lst))
r.headers["content-type"] = [protocol.http.HDR_FORM_URLENCODED]
- assert r.form_urlencoded == d
+ assert r.get_form_urlencoded() == d
d = flow.ODict([("x", "y")])
- r.form_urlencoded = d
- assert r.form_urlencoded == d
+ r.set_form_urlencoded(d)
+ assert r.get_form_urlencoded() == d
r.headers["content-type"] = ["foo"]
- assert not r.form_urlencoded
+ assert not r.get_form_urlencoded()
def test_getset_query(self):
h = flow.ODictCaseless()
r = tutils.treq()
r.path = "/foo?x=y&a=b"
- q = r.query
+ q = r.get_query()
assert q.lst == [("x", "y"), ("a", "b")]
r.path = "/"
- q = r.query
+ q = r.get_query()
assert not q
r.path = "/?adsfa"
- q = r.query
+ q = r.get_query()
assert q.lst == [("adsfa", "")]
r.path = "/foo?x=y&a=b"
- assert r.query
- r.query = flow.ODict([])
- assert not r.query
+ assert r.get_query()
+ r.set_query(flow.ODict([]))
+ assert not r.get_query()
qv = flow.ODict([("a", "b"), ("c", "d")])
- r.query = qv
- assert r.query == qv
+ r.set_query(qv)
+ assert r.get_query() == qv
def test_anticache(self):
h = flow.ODictCaseless()
@@ -916,14 +916,14 @@ class TestRequest:
h = flow.ODictCaseless()
r = tutils.treq()
r.headers = h
- assert r.cookies is None
+ assert r.get_cookies() is None
def test_get_cookies_single(self):
h = flow.ODictCaseless()
h["Cookie"] = ["cookiename=cookievalue"]
r = tutils.treq()
r.headers = h
- result = r.cookies
+ result = r.get_cookies()
assert len(result)==1
assert result['cookiename']==('cookievalue',{})
@@ -932,7 +932,7 @@ class TestRequest:
h["Cookie"] = ["cookiename=cookievalue;othercookiename=othercookievalue"]
r = tutils.treq()
r.headers = h
- result = r.cookies
+ result = r.get_cookies()
assert len(result)==2
assert result['cookiename']==('cookievalue',{})
assert result['othercookiename']==('othercookievalue',{})
@@ -942,7 +942,7 @@ class TestRequest:
h["Cookie"] = ["cookiename=coo=kievalue;othercookiename=othercookievalue"]
r = tutils.treq()
r.headers = h
- result = r.cookies
+ result = r.get_cookies()
assert len(result)==2
assert result['cookiename']==('coo=kievalue',{})
assert result['othercookiename']==('othercookievalue',{})
@@ -966,18 +966,18 @@ class TestResponse:
def test_simple(self):
f = tutils.tflow(resp=True)
resp = f.response
- assert resp._assemble()
- assert resp.size() == len(resp._assemble())
+ assert resp.assemble()
+ assert resp.size() == len(resp.assemble())
resp2 = resp.copy()
assert resp2 == resp
resp.content = None
- assert resp._assemble()
- assert resp.size() == len(resp._assemble())
+ assert resp.assemble()
+ assert resp.size() == len(resp.assemble())
resp.content = CONTENT_MISSING
- tutils.raises("Cannot assemble flow with CONTENT_MISSING", resp._assemble)
+ tutils.raises("Cannot assemble flow with CONTENT_MISSING", resp.assemble)
def test_refresh(self):
r = tutils.tresp()
@@ -1052,14 +1052,14 @@ class TestResponse:
h = flow.ODictCaseless()
resp = tutils.tresp()
resp.headers = h
- assert not resp.cookies
+ assert not resp.get_cookies()
def test_get_cookies_simple(self):
h = flow.ODictCaseless()
h["Set-Cookie"] = ["cookiename=cookievalue"]
resp = tutils.tresp()
resp.headers = h
- result = resp.cookies
+ result = resp.get_cookies()
assert len(result)==1
assert "cookiename" in result
assert result["cookiename"] == ("cookievalue", {})
@@ -1069,7 +1069,7 @@ class TestResponse:
h["Set-Cookie"] = ["cookiename=cookievalue;domain=example.com;expires=Wed Oct 21 16:29:41 2015;path=/; HttpOnly"]
resp = tutils.tresp()
resp.headers = h
- result = resp.cookies
+ result = resp.get_cookies()
assert len(result)==1
assert "cookiename" in result
assert result["cookiename"][0] == "cookievalue"
@@ -1084,7 +1084,7 @@ class TestResponse:
h["Set-Cookie"] = ["cookiename=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/"]
resp = tutils.tresp()
resp.headers = h
- result = resp.cookies
+ result = resp.get_cookies()
assert len(result)==1
assert "cookiename" in result
assert result["cookiename"][0] == ""
@@ -1095,7 +1095,7 @@ class TestResponse:
h["Set-Cookie"] = ["cookiename=cookievalue","othercookie=othervalue"]
resp = tutils.tresp()
resp.headers = h
- result = resp.cookies
+ result = resp.get_cookies()
assert len(result)==2
assert "cookiename" in result
assert result["cookiename"] == ("cookievalue", {})
diff --git a/test/test_protocol_http.py b/test/test_protocol_http.py
index 41019672..ea6cf3fd 100644
--- a/test/test_protocol_http.py
+++ b/test/test_protocol_http.py
@@ -31,7 +31,7 @@ class TestHTTPRequest:
f.request.host = f.server_conn.address.host
f.request.port = f.server_conn.address.port
f.request.scheme = "http"
- assert f.request._assemble() == "OPTIONS * HTTP/1.1\r\nHost: address:22\r\n\r\n"
+ assert f.request.assemble() == "OPTIONS * HTTP/1.1\r\nHost: address:22\r\n\r\n"
def test_origin_form(self):
s = StringIO("GET /foo\xff HTTP/1.1")
@@ -59,7 +59,7 @@ class TestHTTPRequest:
s = StringIO("CONNECT address:22 HTTP/1.1")
r = HTTPRequest.from_stream(s)
r.scheme, r.host, r.port = "http", "address", 22
- assert r._assemble() == "CONNECT address:22 HTTP/1.1\r\nHost: address:22\r\n\r\n"
+ assert r.assemble() == "CONNECT address:22 HTTP/1.1\r\nHost: address:22\r\n\r\n"
assert r.pretty_url(False) == "address:22"
def test_absolute_form(self):
@@ -67,11 +67,11 @@ class TestHTTPRequest:
tutils.raises("Bad HTTP request line", HTTPRequest.from_stream, s)
s = StringIO("GET http://address:22/ HTTP/1.1")
r = HTTPRequest.from_stream(s)
- assert r._assemble() == "GET http://address:22/ HTTP/1.1\r\nHost: address:22\r\n\r\n"
+ assert r.assemble() == "GET http://address:22/ HTTP/1.1\r\nHost: address:22\r\n\r\n"
def test_assemble_unknown_form(self):
r = tutils.treq()
- tutils.raises("Invalid request form", r._assemble, "antiauthority")
+ tutils.raises("Invalid request form", r.assemble, "antiauthority")
def test_set_url(self):
r = tutils.treq_absolute()
diff --git a/test/test_proxy.py b/test/test_proxy.py
index e65841f4..073e76b5 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -28,7 +28,7 @@ class TestServerConnection:
f = tutils.tflow()
f.server_conn = sc
f.request.path = "/p/200:da"
- sc.send(f.request._assemble())
+ sc.send(f.request.assemble())
assert http.read_response(sc.rfile, f.request.method, 1000)
assert self.d.last_log()