Chromium Code Reviews| Index: appengine/isolate/handlers_frontend.py |
| diff --git a/appengine/isolate/handlers_frontend.py b/appengine/isolate/handlers_frontend.py |
| index 531dc7ae2b14d52565f38712f278f9dee4c6f2a1..f573bbd928e131442986d7522e1945f8fac30377 100644 |
| --- a/appengine/isolate/handlers_frontend.py |
| +++ b/appengine/isolate/handlers_frontend.py |
| @@ -8,7 +8,9 @@ import cgi |
| import datetime |
| import json |
| import logging |
| +import os |
| import re |
| +import urllib |
| import webapp2 |
| @@ -58,6 +60,16 @@ _GVIZ_COLUMNS_ORDER = ( |
| 'contains_lookups', |
| ) |
| +_ISOLATED_ROOT_MEMBERS = ( |
| + 'algo', |
| + 'command', |
| + 'files', |
| + 'includes', |
| + 'read_only', |
| + 'relative_cwd', |
| + 'version' |
|
M-A Ruel
2017/02/14 23:46:14
include trailing comma, so when a new line is adde
jonesmi
2017/02/16 19:17:30
Done.
|
| +) |
| + |
| ### Restricted handlers |
| @@ -191,7 +203,9 @@ class BrowseHandler(auth.AuthenticatingHandler): |
| namespace = self.request.get('namespace', 'default-gzip') |
| # Support 'hash' for compatibility with old links. To remove eventually. |
| digest = self.request.get('digest', '') or self.request.get('hash', '') |
| + save_as = self.request.get('as', '') |
| params = { |
| + u'as': unicode(save_as), |
| u'digest': unicode(digest), |
| u'namespace': unicode(namespace), |
| } |
| @@ -258,33 +272,61 @@ class ContentHandler(auth.AuthenticatingHandler): |
| ' python isolateserver.py download -I %s --namespace %s -f %s %s' |
| % (sizeInMib, host, namespace, digest, digest)) |
| else: |
| - self.response.headers['Content-Disposition'] = str('filename=%s' |
| - % digest) |
| - if content.startswith('{'): |
| - # Try to format as JSON. |
| - try: |
| - content = json.dumps( |
| - json.loads(content), sort_keys=True, indent=2, |
| - separators=(',', ': ')) |
| - content = cgi.escape(content) |
| - # If we don't wrap this in html, browsers will put content in a pre |
| - # tag which is also styled with monospace/pre-wrap. We can't use |
| - # anchor tags in <pre>, so we force it to be a <div>, which happily |
| - # accepts links. |
| - content = ( |
| - '<div style="font-family:monospace;white-space:pre-wrap;">%s' |
| - '</div>' % content) |
| - # Linkify things that look like hashes |
| - content = re.sub(r'([0-9a-f]{40})', |
| - r'<a target="_blank" href="/browse?namespace=%s' % namespace + |
| - r'&digest=\1">\1</a>', |
| - content) |
| - self.response.headers['Content-Type'] = 'text/html; charset=utf-8' |
| - except ValueError: |
| - pass |
| + self.response.headers['Content-Disposition'] = str( |
| + 'filename=%s' % self.request.get('as') or digest) |
| + if self._is_isolated_format(content): |
| + content = self._format_isolated_content(content, namespace) |
| + self.response.headers['Content-Type'] = 'text/html; charset=utf-8' |
| self.response.write(content) |
| + @staticmethod |
| + def _is_isolated_format(content): |
| + """Checks if content string is a valid .isolated format.""" |
| + try: |
| + data = json.loads(content) |
| + except ValueError: |
| + return False |
| + if isinstance(data, dict): |
| + actual = set(data) |
| + if actual.issubset(_ISOLATED_ROOT_MEMBERS) and 'files' in actual: |
|
M-A Ruel
2017/02/14 23:46:14
return actual.issubset(_ISOLATED_ROOT_MEMBERS) and
jonesmi
2017/02/16 19:17:30
Done.
|
| + return True |
| + return False |
| + |
| + @staticmethod |
| + def _format_isolated_content(content, namespace): |
|
mithro
2017/02/16 00:50:52
I feel like just giving the isolate json to a temp
|
| + """Formats .isolated content and returns a string representation of it.""" |
| + try: |
| + # Ensure we're working with HTML-safe content. Do this before adding |
| + # our own hyperlinks because cgi.escape would replace our anchor symbols. |
| + content = cgi.escape(content) |
| + data = json.loads(content) |
| + except ValueError: |
| + return content |
| + |
| + # Linkify all files |
| + if 'files' in data: |
| + hyperlinked_files = {} |
| + for filepath, metadata in data['files'].iteritems(): |
| + if 'h' in metadata: # Only linkify files that have a digest property |
|
M-A Ruel
2017/02/14 23:46:14
if metadata.get('h'):
is safer. no need for the co
jonesmi
2017/02/16 19:17:30
Done
|
| + save_as = os.path.basename(filepath) |
| + anchor = (r'<a target=_blank" ' |
| + r'href=/browse?namespace=%s&digest=%s&as=%s>' |
| + r'%s</a>') % ( |
| + namespace, metadata['h'], urllib.quote(save_as), filepath) |
| + hyperlinked_files[anchor] = metadata |
| + data['files'] = hyperlinked_files |
| + |
| + content = json.dumps(data, sort_keys=True, indent=2, separators=(',', ': ')) |
| + # If we don't wrap this in html, browsers will put content in a pre |
| + # tag which is also styled with monospace/pre-wrap. We can't use |
| + # anchor tags in <pre>, so we force it to be a <div>, which happily |
| + # accepts links. |
| + content = ( |
| + '<div style="font-family:monospace;white-space:pre-wrap;">%s' |
| + '</div>' % content) |
| + return content |
| + |
| class StatsHandler(webapp2.RequestHandler): |
| """Returns the statistics web page.""" |