Created
September 20, 2011 17:47
-
-
Save joeshaw/1229770 to your computer and use it in GitHub Desktop.
Revisions
-
joeshaw created this gist
Sep 20, 2011 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,113 @@ # coding:utf-8 # Copyright 2011 litl, LLC. All Rights Reserved. import httplib import re import urllib import urlparse from flask import Blueprint, request, Response, url_for from werkzeug.datastructures import Headers from werkzeug.exceptions import NotFound from jiffy.admin.login import check_login proxy = Blueprint('proxy', __name__) proxy.before_request(check_login) HTML_REGEX = re.compile(r'((?:src|action|href)=["\'])/') JQUERY_REGEX = re.compile(r'(\$\.(?:get|post)\(["\'])/') JS_LOCATION_REGEX = re.compile(r'((?:window|document)\.location.*=.*["\'])/') CSS_REGEX = re.compile(r'(url\(["\']?)/') REGEXES = [HTML_REGEX, JQUERY_REGEX, JS_LOCATION_REGEX, CSS_REGEX] def iterform(multidict): for key in multidict.keys(): for value in multidict.getlist(key): yield (key.encode("utf8"), value.encode("utf8")) def parse_host_port(h): """Parses strings in the form host[:port]""" host_port = h.split(":", 1) if len(host_port) == 1: return (h, 80) else: host_port[1] = int(host_port[1]) return host_port @proxy.route('/proxy/<host>/', methods=["GET", "POST"]) @proxy.route('/proxy/<host>/<path:file>', methods=["GET", "POST"]) def proxy_request(host, file=""): hostname, port = parse_host_port(host) # Whitelist a few headers to pass on request_headers = {} for h in ["Cookie", "Referer", "X-Csrf-Token"]: if h in request.headers: request_headers[h] = request.headers[h] if request.query_string: path = "/%s?%s" % (file, request.query_string) else: path = "/" + file if request.method == "POST": form_data = list(iterform(request.form)) form_data = urllib.urlencode(form_data) request_headers["Content-Length"] = len(form_data) else: form_data = None conn = httplib.HTTPConnection(hostname, port) conn.request(request.method, path, body=form_data, headers=request_headers) resp = conn.getresponse() # Clean up response headers for forwarding response_headers = Headers() for key, value in resp.getheaders(): if key in ["content-length", "connection", "content-type"]: continue if key == "set-cookie": cookies = value.split(",") [response_headers.add(key, c) for c in cookies] else: response_headers.add(key, value) # If this is a redirect, munge the Location URL if "location" in response_headers: redirect = response_headers["location"] parsed = urlparse.urlparse(request.url) redirect_parsed = urlparse.urlparse(redirect) redirect_host = redirect_parsed.netloc if not redirect_host: redirect_host = "%s:%d" % (hostname, port) redirect_path = redirect_parsed.path if redirect_parsed.query: redirect_path += "?" + redirect_parsed.query munged_path = url_for(".proxy_request", host=redirect_host, file=redirect_path[1:]) url = "%s://%s%s" % (parsed.scheme, parsed.netloc, munged_path) response_headers["location"] = url # Rewrite URLs in the content to point to our URL scheme instead. # Ugly, but seems to mostly work. root = url_for(".proxy_request", host=host) contents = resp.read() for regex in REGEXES: contents = regex.sub(r'\1%s' % root, contents) flask_response = Response(response=contents, status=resp.status, headers=response_headers, content_type=resp.getheader('content-type')) return flask_response