From ebd539b49f0706918e979dc921cf454ae448eaf9 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 8 Sep 2014 16:02:31 +0200 Subject: [PATCH] update examples, fix #353 --- examples/README | 33 +++++++++++++++------- examples/add_header.py | 2 +- examples/change_upstream_proxy.py | 23 +++++++-------- examples/dup_and_replay.py | 6 ++-- examples/flowbasic | 3 +- examples/iframe_injector.py | 10 +++---- examples/modify_form.py | 2 +- examples/modify_querystring.py | 2 +- examples/modify_response_body.py | 8 +++--- examples/nonblocking.py | 2 +- examples/proxapp | 47 ------------------------------- examples/proxapp.py | 24 ++++++++++++++++ examples/read_dumpfile | 11 ++++---- examples/redirect_requests.py | 6 +++- examples/stickycookies | 28 +++++++++--------- examples/stream.py | 2 +- examples/stub.py | 36 +++++++++++------------ examples/upsidedownternet.py | 20 ++++++++----- libmproxy/protocol/http.py | 3 +- libmproxy/script.py | 4 +++ 20 files changed, 140 insertions(+), 132 deletions(-) delete mode 100755 examples/proxapp create mode 100644 examples/proxapp.py diff --git a/examples/README b/examples/README index 46d25acd2..85ab272ae 100644 --- a/examples/README +++ b/examples/README @@ -1,10 +1,23 @@ -add_header.py Simple script that just adds a header to every request. -dup_and_replay.py Duplicates each request, changes it, and then replays the modified request. -flowbasic Basic use of mitmproxy as a library. -modify_form.py Modify all form submissions to add a parameter. -modify_querystring.py Modify all query strings to add a parameters. -proxapp How to embed a WSGI app in a mitmproxy server -stub.py Script stub with a method definition for every event. -stickycookies An example of writing a custom proxy with libmproxy. -upsidedownternet.py Rewrites traffic to turn PNGs upside down. -mitmproxywrapper.py Bracket mitmproxy run with proxy enable/disable on OS X +# inline script examples +add_header.py Simple script that just adds a header to every request. +change_upstream_proxy.py Dynamically change the upstream proxy +dup_and_replay.py Duplicates each request, changes it, and then replays the modified request. +iframe_injector.py Inject configurable iframe into pages. +modify_form.py Modify all form submissions to add a parameter. +modify_querystring.py Modify all query strings to add a parameters. +modify_response_body.py Replace arbitrary strings in all responses +nonblocking.py Demonstrate parallel processing with a blocking script. +proxapp.py How to embed a WSGI app in a mitmproxy server +redirect_requests.py Redirect requests or directly reply to them. +stub.py Script stub with a method definition for every event. +upsidedownternet.py Rewrites traffic to turn images upside down. + + +# libmproxy examples +flowbasic Basic use of mitmproxy as a library. +stickycookies An example of writing a custom proxy with libmproxy. + + +# misc +read_dumpfile Read a dumpfile generated by mitmproxy. +mitmproxywrapper.py Bracket mitmproxy run with proxy enable/disable on OS X diff --git a/examples/add_header.py b/examples/add_header.py index b9c8c1c66..291741cb0 100644 --- a/examples/add_header.py +++ b/examples/add_header.py @@ -1,2 +1,2 @@ -def response(ctx, flow): +def response(context, flow): flow.response.headers["newheader"] = ["foo"] \ No newline at end of file diff --git a/examples/change_upstream_proxy.py b/examples/change_upstream_proxy.py index 86031d29d..e063ca4fa 100644 --- a/examples/change_upstream_proxy.py +++ b/examples/change_upstream_proxy.py @@ -4,17 +4,18 @@ # Usage: mitmdump -s "change_upstream_proxy.py host" from libmproxy.protocol.http import send_connect_request +alternative_upstream_proxy = ("localhost", 8082) def should_redirect(flow): - return (flow.request.host == "example.com") -alternative_upstream_proxy = ("localhost",8082) + return flow.request.host == "example.com" -def request(ctx, flow): - if flow.live and should_redirect(flow): - # If you want to change the target server, you should modify flow.request.host and flow.request.port - # flow.live.change_server should only be used by inline scripts to change the upstream proxy, - # unless you are sure that you know what you are doing. - server_changed = flow.live.change_server(alternative_upstream_proxy, persistent_change=True) - if flow.request.scheme == "https" and server_changed: - send_connect_request(flow.live.c.server_conn, flow.request.host, flow.request.port) - flow.live.c.establish_ssl(server=True) +def request(context, flow): + if flow.live and should_redirect(flow): + + # If you want to change the target server, you should modify flow.request.host and flow.request.port + # flow.live.change_server should only be used by inline scripts to change the upstream proxy, + # unless you are sure that you know what you are doing. + server_changed = flow.live.change_server(alternative_upstream_proxy, persistent_change=True) + if flow.request.scheme == "https" and server_changed: + send_connect_request(flow.live.c.server_conn, flow.request.host, flow.request.port) + flow.live.c.establish_ssl(server=True) diff --git a/examples/dup_and_replay.py b/examples/dup_and_replay.py index b38c2b7e3..3d9279ccf 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) +def request(context, flow): + f = context.duplicate_flow(flow) f.request.path = "/changed" - ctx.replay_request(f) \ No newline at end of file + context.replay_request(f) \ No newline at end of file diff --git a/examples/flowbasic b/examples/flowbasic index 2b44be3fc..9aeb83c5f 100755 --- a/examples/flowbasic +++ b/examples/flowbasic @@ -12,6 +12,7 @@ import os from libmproxy import flow, proxy from libmproxy.proxy.server import ProxyServer + class MyMaster(flow.FlowMaster): def run(self): try: @@ -34,7 +35,7 @@ class MyMaster(flow.FlowMaster): config = proxy.ProxyConfig( - ca_file = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem") + ca_file=os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem") ) state = flow.State() server = ProxyServer(config, 8080) diff --git a/examples/iframe_injector.py b/examples/iframe_injector.py index 7042dbab7..42260a7e3 100644 --- a/examples/iframe_injector.py +++ b/examples/iframe_injector.py @@ -3,16 +3,16 @@ from libmproxy.protocol.http import decoded -def start(ctx, argv): +def start(context, argv): if len(argv) != 2: raise ValueError('Usage: -s "iframe_injector.py url"') - ctx.iframe_url = argv[1] + context.iframe_url = argv[1] -def handle_response(ctx, flow): +def handle_response(context, flow): with decoded(flow.response): # Remove content encoding (gzip, ...) c = flow.response.replace( '', - '' % ctx.iframe_url) + '' % context.iframe_url) if c > 0: - ctx.log("Iframe injected!") \ No newline at end of file + context.log("Iframe injected!") \ No newline at end of file diff --git a/examples/modify_form.py b/examples/modify_form.py index 6d651b196..3d93e392d 100644 --- a/examples/modify_form.py +++ b/examples/modify_form.py @@ -1,5 +1,5 @@ -def request(ctx, flow): +def request(context, flow): if "application/x-www-form-urlencoded" in flow.request.headers["content-type"]: form = flow.request.get_form_urlencoded() form["mitmproxy"] = ["rocks"] diff --git a/examples/modify_querystring.py b/examples/modify_querystring.py index 56fbbb32a..1dd4807ae 100644 --- a/examples/modify_querystring.py +++ b/examples/modify_querystring.py @@ -1,5 +1,5 @@ -def request(ctx, flow): +def request(context, flow): q = flow.request.get_query() if q: q["mitmproxy"] = ["rocks"] diff --git a/examples/modify_response_body.py b/examples/modify_response_body.py index e8428cdbc..4afd04214 100644 --- a/examples/modify_response_body.py +++ b/examples/modify_response_body.py @@ -3,13 +3,13 @@ from libmproxy.protocol.http import decoded -def start(ctx, argv): +def start(context, argv): if len(argv) != 3: raise ValueError('Usage: -s "modify-response-body.py old new"') # You may want to use Python's argparse for more sophisticated argument parsing. - ctx.old, ctx.new = argv[1], argv[2] + context.old, context.new = argv[1], argv[2] -def response(ctx, flow): +def response(context, flow): with decoded(flow.response): # automatically decode gzipped responses. - flow.response.content = flow.response.content.replace(ctx.old, ctx.new) \ No newline at end of file + flow.response.content = flow.response.content.replace(context.old, context.new) \ No newline at end of file diff --git a/examples/nonblocking.py b/examples/nonblocking.py index 1396742aa..481c0407f 100644 --- a/examples/nonblocking.py +++ b/examples/nonblocking.py @@ -2,7 +2,7 @@ import time from libmproxy.script import concurrent -@concurrent +@concurrent # Remove this and see what happens def request(context, flow): print "handle request: %s%s" % (flow.request.host, flow.request.path) time.sleep(5) diff --git a/examples/proxapp b/examples/proxapp deleted file mode 100755 index 9f299d259..000000000 --- a/examples/proxapp +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -""" -This example shows how to graft a WSGI app onto mitmproxy. In this -instance, we're using the Bottle framework (http://bottlepy.org/) to expose -a single simplest-possible page. -""" -import bottle -import os -from libmproxy import proxy, flow - -@bottle.route('/') -def index(): - return 'Hi!' - - -class MyMaster(flow.FlowMaster): - def run(self): - try: - flow.FlowMaster.run(self) - except KeyboardInterrupt: - self.shutdown() - - def handle_request(self, f): - f = flow.FlowMaster.handle_request(self, f) - if f: - f.reply() - return f - - def handle_response(self, f): - f = flow.FlowMaster.handle_response(self, f) - if f: - f.reply() - print f - return f - - -config = proxy.ProxyConfig( - cacert = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem") -) -state = flow.State() -server = proxy.ProxyServer(config, 8080) -# Register the app using the magic domain "proxapp" on port 80. Requests to -# this domain and port combination will now be routed to the WSGI app instance. -server.apps.add(bottle.app(), "proxapp", 80) -m = MyMaster(server, state) -m.run() - diff --git a/examples/proxapp.py b/examples/proxapp.py new file mode 100644 index 000000000..d777d5225 --- /dev/null +++ b/examples/proxapp.py @@ -0,0 +1,24 @@ +""" +This example shows how to graft a WSGI app onto mitmproxy. In this +instance, we're using the Flask framework (http://flask.pocoo.org/) to expose +a single simplest-possible page. +""" +from flask import Flask + +app = Flask("proxapp") + + +@app.route('/') +def hello_world(): + return 'Hello World!' + + +# Register the app using the magic domain "proxapp" on port 80. Requests to +# this domain and port combination will now be routed to the WSGI app instance. +def start(context, argv): + context.app_registry.add(app, "proxapp", 80) + + # SSL works too, but the magic domain needs to be resolvable from the mitmproxy machine due to mitmproxy's design. + # mitmproxy will connect to said domain and use serve its certificate (unless --no-upstream-cert is set) + # but won't send any data. + context.app_registry.add(app, "example.com", 443) \ No newline at end of file diff --git a/examples/read_dumpfile b/examples/read_dumpfile index 547f3c17f..c0a50d26a 100644 --- a/examples/read_dumpfile +++ b/examples/read_dumpfile @@ -6,12 +6,13 @@ from libmproxy import flow import json, sys -with open("logfile", "rb") as f: - freader = flow.FlowReader(f) +with open("logfile", "rb") as logfile: + freader = flow.FlowReader(logfile) try: - for i in freader.stream(): - print i.request.host - json.dump(i._get_state(), sys.stdout, indent=4) + for f in freader.stream(): + print(f) + print(f.request.host) + json.dump(f._get_state(), sys.stdout, indent=4) print "" except flow.FlowReadError, v: print "Flow file corrupted. Stopped loading." \ No newline at end of file diff --git a/examples/redirect_requests.py b/examples/redirect_requests.py index c5561839c..d9a3bfc5b 100644 --- a/examples/redirect_requests.py +++ b/examples/redirect_requests.py @@ -6,15 +6,19 @@ This example shows two ways to redirect flows to other destinations. """ -def request(ctx, flow): +def request(context, 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. + + # Method 1: Answer with a locally generated response if flow.request.pretty_host(hostheader=True).endswith("example.com"): resp = HTTPResponse( [1, 1], 200, "OK", ODictCaseless([["Content-Type", "text/html"]]), "helloworld") flow.reply(resp) + + # Method 2: Redirect the request to a different server if flow.request.pretty_host(hostheader=True).endswith("example.org"): flow.request.host = "mitmproxy.org" flow.request.update_host_header() diff --git a/examples/stickycookies b/examples/stickycookies index 2aab31d68..d2368e22f 100755 --- a/examples/stickycookies +++ b/examples/stickycookies @@ -1,9 +1,9 @@ #!/usr/bin/env python """ This example builds on mitmproxy's base proxying infrastructure to -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. +implement functionality similar to the "sticky cookies" option. + +Heads Up: In the majority of cases, you want to use inline scripts. """ import os from libmproxy import controller, proxy @@ -21,19 +21,19 @@ class StickyMaster(controller.Master): except KeyboardInterrupt: self.shutdown() - def handle_request(self, msg): - hid = (msg.host, msg.port) - if msg.headers["cookie"]: - self.stickyhosts[hid] = msg.headers["cookie"] + def handle_request(self, flow): + hid = (flow.request.host, flow.request.port) + if flow.request.headers["cookie"]: + self.stickyhosts[hid] = flow.request.headers["cookie"] elif hid in self.stickyhosts: - msg.headers["cookie"] = self.stickyhosts[hid] - msg.reply() + flow.request.headers["cookie"] = self.stickyhosts[hid] + flow.reply() - def handle_response(self, msg): - hid = (msg.request.host, msg.request.port) - if msg.headers["set-cookie"]: - self.stickyhosts[hid] = msg.headers["set-cookie"] - msg.reply() + def handle_response(self, flow): + hid = (flow.request.host, flow.request.port) + if flow.response.headers["set-cookie"]: + self.stickyhosts[hid] = flow.response.headers["set-cookie"] + flow.reply() config = proxy.ProxyConfig( diff --git a/examples/stream.py b/examples/stream.py index f9f03c3ea..7d5efc1e9 100644 --- a/examples/stream.py +++ b/examples/stream.py @@ -1,4 +1,4 @@ -def responseheaders(ctx, flow): +def responseheaders(context, flow): """ Enables streaming for all responses. """ diff --git a/examples/stub.py b/examples/stub.py index 5976dd767..c5cdad9cc 100644 --- a/examples/stub.py +++ b/examples/stub.py @@ -1,63 +1,63 @@ """ This is a script stub, with definitions for all events. """ -def start(ctx, argv): +def start(context, argv): """ Called once on script startup, before any other events. """ - ctx.log("start") + context.log("start") -def clientconnect(ctx, conn_handler): +def clientconnect(context, 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") + context.log("clientconnect") -def serverconnect(ctx, conn_handler): +def serverconnect(context, conn_handler): """ Called when the proxy initiates a connection to the target server. Note that a connection can correspond to multiple HTTP requests """ - ctx.log("serverconnect") + context.log("serverconnect") -def request(ctx, flow): +def request(context, flow): """ Called when a client request has been received. """ - ctx.log("request") + context.log("request") -def responseheaders(ctx, flow): +def responseheaders(context, flow): """ Called when the response headers for a server response have been received, but the response body has not been processed yet. Can be used to tell mitmproxy to stream the response. """ - ctx.log("responseheaders") + context.log("responseheaders") -def response(ctx, flow): +def response(context, flow): """ Called when a server response has been received. """ - ctx.log("response") + context.log("response") -def error(ctx, flow): +def error(context, flow): """ Called when a flow error has occured, 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. """ - ctx.log("error") + context.log("error") -def clientdisconnect(ctx, conn_handler): +def clientdisconnect(context, conn_handler): """ Called when a client disconnects from the proxy. """ - ctx.log("clientdisconnect") + context.log("clientdisconnect") -def done(ctx): +def done(context): """ Called once on script shutdown, after any other events. """ - ctx.log("done") + context.log("done") diff --git a/examples/upsidedownternet.py b/examples/upsidedownternet.py index a52b6d300..738eb11ff 100644 --- a/examples/upsidedownternet.py +++ b/examples/upsidedownternet.py @@ -1,10 +1,16 @@ import cStringIO from PIL import Image +from libmproxy.protocol.http import decoded -def response(ctx, flow): - if flow.response.headers["content-type"] == ["image/png"]: - s = cStringIO.StringIO(flow.response.content) - img = Image.open(s).rotate(180) - s2 = cStringIO.StringIO() - img.save(s2, "png") - flow.response.content = s2.getvalue() +def response(context, flow): + if flow.response.headers.get_first("content-type", "").startswith("image"): + with decoded(flow.response): # automatically decode gzipped responses. + try: + s = cStringIO.StringIO(flow.response.content) + img = Image.open(s).rotate(180) + s2 = cStringIO.StringIO() + img.save(s2, "png") + flow.response.content = s2.getvalue() + flow.response.headers["content-type"] = ["image/png"] + except: # Unknown image types etc. + pass \ No newline at end of file diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py index 35c5611f1..5579cb631 100644 --- a/libmproxy/protocol/http.py +++ b/libmproxy/protocol/http.py @@ -1095,7 +1095,8 @@ class HTTPHandler(ProtocolHandler): if request.form_in == "absolute": if request.scheme != "http": raise http.HttpError(400, "Invalid request scheme: %s" % request.scheme) - if request.form_out == "relative": + if self.c.config.mode == "regular": + # Update info so that an inline script sees the correct value at flow.server_conn self.c.set_server_address((request.host, request.port)) flow.server_conn = self.c.server_conn diff --git a/libmproxy/script.py b/libmproxy/script.py index 79151f157..b559615b6 100644 --- a/libmproxy/script.py +++ b/libmproxy/script.py @@ -38,6 +38,10 @@ class ScriptContext: """ self._master.replay_request(f) + @property + def app_registry(self): + return self._master.apps + class Script: """