blob: 18e9de1f4b850ce4bfb60d15e95449dbf9b07600 [file] [log] [blame]
# 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 re
import argparse
import os.path
import json
import mimetypes
class Server:
def __init__(self, devpath, plugins, pluginroot, assets, strip_assets, theme):
if devpath:
print("Serving app from " + devpath)
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.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"></script>'
pos = flow.response.text.find(marker)
if pos != -1:
pos += len(marker)
flow.response.text = ''.join([
flow.response.text[:pos],
'<link rel="import" href="/gerrit_assets/123.0/' + server.assets_file + '">',
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 = ""
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]
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)
flow.response.content = server.readfile(localfile)
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("--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", type=str, help="Path to the default site theme to be used.")
args = parser.parse_args()
server = Server(expandpath(args.app) + '/',
args.plugins,
expandpath(args.plugin_root) + '/',
args.assets and expandpath(args.assets),
args.strip_assets,
expandpath(args.theme))