# 1. install and setup mitmproxy v2.0.2: https://mitmproxy.readthedocs.io/en/v2.0.2/install.html # (In case of python versions trouble, use https://www.anaconda.com/) # 2. mitmdump -q -s -p 8888 \ # "serve-app-dev.py --app /path/to/polygerrit-ui/app/" # 3. start Chrome with --proxy-server="127.0.0.1:8888" --user-data-dir=/tmp/devchrome # 4. open, say, gerrit-review.googlesource.com. Or chromium-review.googlesource.com. Any. # 5. uncompiled source files are served and you can log in, too. # 6. enjoy! # # P.S. For replacing plugins, use --plugins or --plugin_root # # --plugin takes comma-separated list of plugins to add or replace. # # Example: Adding a new plugin to the server response: # --plugins ~/gerrit-testsite/plugins/myplugin.html # # Example: Replace all matching plugins with local versions: # --plugins ~/gerrit-testsite/plugins/ # Following files will be served if they exist for /plugins/tricium/static/tricium.html: # ~/gerrit-testsite/plugins/tricium.html # ~/gerrit-testsite/plugins/tricium/static/tricium.html # # --assets takes assets bundle.html, expecting rest of the assets files to be in the same folder # # Example: # --assets ~/gerrit-testsite/assets/a3be19f.html # from mitmproxy import http from mitmproxy.script import concurrent import argparse import json import mimetypes import os.path import re import zipfile class Server: def __init__(self, devpath, components, plugins, pluginroot, assets, strip_assets, theme): if devpath: print("Serving app from " + devpath) if components: print("Serving components from " + components) if pluginroot: print("Serving plugins from " + pluginroot) if assets: self.assets_root, self.assets_file = os.path.split(assets) print("Assets: using " + self.assets_file + " from " + self.assets_root) else: self.assets_root = None if plugins: self.plugins = {path.split("/")[-1:][0]: path for path in map(expandpath, plugins.split(","))} for filename, path in self.plugins.items(): print("Serving " + filename + " from " + path) else: self.plugins = {} self.devpath = devpath self.components = components self.pluginroot = pluginroot self.strip_assets = strip_assets self.theme = theme def readfile(self, path): with open(path, 'rb') as contentfile: return contentfile.read() @concurrent def response(flow: http.HTTPFlow) -> None: if server.strip_assets: assets_bundle = 'googlesource.com/polygerrit_assets' assets_pos = flow.response.text.find(assets_bundle) if assets_pos != -1: t = flow.response.text flow.response.text = t[:t.rfind('<', 0, assets_pos)] + t[t.find('>', assets_pos) + 1:] return if server.assets_root: marker = 'webcomponents-lite.js">' pos = flow.response.text.find(marker) if pos != -1: pos += len(marker) flow.response.text = ''.join([ flow.response.text[:pos], '', flow.response.text[pos:] ]) assets_prefix = "/gerrit_assets/123.0/" if flow.request.path.startswith(assets_prefix): assets_file = flow.request.path[len(assets_prefix):] flow.response.content = server.readfile(server.assets_root + '/' + assets_file) flow.response.status_code = 200 if assets_file.endswith('.js'): flow.response.headers['Content-type'] = 'text/javascript' return m = re.match(".+polygerrit_ui/\d+\.\d+/(.+)", flow.request.path) pluginmatch = re.match("^/plugins/(.+)", flow.request.path) localfile = "" content = "" if flow.request.path == "/config/server/info": config = json.loads(flow.response.content[5:].decode('utf8')) if server.theme: config['default_theme'] = '/static/gerrit-theme.html' for filename, path in server.plugins.items(): pluginname = filename.split(".")[0] payload = config["plugin"]["js_resource_paths" if filename.endswith(".js") else "html_resource_paths"] if list(filter(lambda url: filename in url, payload)): continue payload.append("plugins/" + pluginname + "/static/" + filename) flow.response.content = str.encode(")]}'\n" + json.dumps(config)) if m is not None: filepath = m.groups()[0] if (filepath.startswith("bower_components/")): with zipfile.ZipFile(server.components + "test_components.zip") as bower_zip: content = bower_zip.read(filepath) localfile = server.devpath + filepath elif pluginmatch is not None: pluginfile = flow.request.path_components[-1] if server.plugins and pluginfile in server.plugins: if os.path.isfile(server.plugins[pluginfile]): localfile = server.plugins[pluginfile] else: print("Can't find file " + server.plugins[pluginfile] + " for " + flow.request.path) elif server.pluginroot: pluginurl = pluginmatch.groups()[0] if os.path.isfile(server.pluginroot + pluginfile): localfile = server.pluginroot + pluginfile elif os.path.isfile(server.pluginroot + pluginurl): localfile = server.pluginroot + pluginurl if server.theme: if flow.request.path.endswith('/gerrit-theme.html'): localfile = server.theme else: match = re.match("^/static(/[\w\.]+)$", flow.request.path) if match is not None: localfile = os.path.dirname(server.theme) + match.group(1) if localfile and os.path.isfile(localfile): if pluginmatch is not None: print("Serving " + flow.request.path + " from " + localfile) content = server.readfile(localfile) if content: flow.response.content = content flow.response.status_code = 200 localtype = mimetypes.guess_type(localfile) if localtype and localtype[0]: flow.response.headers['Content-type'] = localtype[0] def expandpath(path): return os.path.realpath(os.path.expanduser(path)) parser = argparse.ArgumentParser() parser.add_argument("--app", type=str, default="", help="Path to /polygerrit-ui/app/") parser.add_argument("--components", type=str, default="", help="Path to test_components.zip") parser.add_argument("--plugins", type=str, default="", help="Comma-separated list of plugin files to add/replace") parser.add_argument("--plugin_root", type=str, default="", help="Path containing individual plugin files to replace") parser.add_argument("--assets", type=str, default="", help="Path containing assets file to import.") parser.add_argument("--strip_assets", action="store_true", help="Strip plugin bundles from the response.") parser.add_argument("--theme", default="", type=str, help="Path to the default site theme to be used.") args = parser.parse_args() server = Server(expandpath(args.app) + '/', expandpath(args.components) + '/', args.plugins, expandpath(args.plugin_root) + '/', args.assets and expandpath(args.assets), args.strip_assets, expandpath(args.theme))