Index: chrome/common/extensions/docs/server2/render_servlet.py |
diff --git a/chrome/common/extensions/docs/server2/render_servlet.py b/chrome/common/extensions/docs/server2/render_servlet.py |
index 25b6d3ded6c869c0d5778e87beb888d559412c24..cb0c4dcf49f1a751b1a7ebd1b7449c80d97e1e72 100644 |
--- a/chrome/common/extensions/docs/server2/render_servlet.py |
+++ b/chrome/common/extensions/docs/server2/render_servlet.py |
@@ -5,20 +5,30 @@ |
from fnmatch import fnmatch |
import logging |
import mimetypes |
+import posixpath |
+import traceback |
from urlparse import urlsplit |
from data_source_registry import CreateDataSources |
from file_system import FileNotFoundError |
+from redirector import Redirector |
from servlet import Servlet, Response |
from svn_constants import DOCS_PATH, PUBLIC_TEMPLATE_PATH |
+from third_party.handlebar import Handlebar |
+ |
+ |
+def _MakeHeaders(content_type): |
+ return { |
+ 'x-frame-options': 'sameorigin', |
+ 'content-type': content_type, |
+ 'cache-control': 'max-age=300', |
+ } |
-def _IsBinaryMimetype(mimetype): |
- return any( |
- mimetype.startswith(prefix) for prefix in ['audio', 'image', 'video']) |
class RenderServlet(Servlet): |
'''Servlet which renders templates. |
''' |
+ |
class Delegate(object): |
def CreateServerInstance(self): |
raise NotImplementedError(self.__class__) |
@@ -33,82 +43,61 @@ class RenderServlet(Servlet): |
# TODO(kalman): a consistent path syntax (even a Path class?) so that we |
# can stop being so conservative with stripping and adding back the '/'s. |
path = self._request.path.lstrip('/') |
- |
- if path.split('/')[-1] == 'redirects.json': |
- return Response.Ok('') |
- |
server_instance = self._delegate.CreateServerInstance() |
- redirect = server_instance.redirector.Redirect(self._request.host, path) |
- if redirect is not None: |
- return Response.Redirect(redirect) |
- |
- canonical_result = server_instance.path_canonicalizer.Canonicalize(path) |
- redirect = canonical_result.path.lstrip('/') |
- if path != redirect: |
- return Response.Redirect('/' + redirect, |
- permanent=canonical_result.permanent) |
- |
- trunk_fs = server_instance.host_file_system_provider.GetTrunk() |
- template_renderer = server_instance.template_renderer |
- template_cache = server_instance.compiled_fs_factory.ForTemplates(trunk_fs) |
- |
- content = None |
- content_type = None |
- |
try: |
- # At this point, any valid paths ending with '/' have been redirected. |
- # Therefore, the response should be a 404 Not Found. |
- if path.endswith('/'): |
- pass |
- elif fnmatch(path, 'extensions/examples/*.zip'): |
- zip_path = DOCS_PATH + path[len('extensions'):-len('.zip')] |
- content = server_instance.directory_zipper.Zip(zip_path).Get() |
- content_type = 'application/zip' |
- elif path.startswith('extensions/examples/'): |
- mimetype = mimetypes.guess_type(path)[0] or 'text/plain' |
- content = trunk_fs.ReadSingle( |
- '%s/%s' % (DOCS_PATH, path[len('extensions/'):]), |
- binary=_IsBinaryMimetype(mimetype)).Get() |
- content_type = mimetype |
- elif path.startswith('static/'): |
- mimetype = mimetypes.guess_type(path)[0] or 'text/plain' |
- content = trunk_fs.ReadSingle( |
- '%s/%s' % (DOCS_PATH, path), |
- binary=_IsBinaryMimetype(mimetype)).Get() |
- content_type = mimetype |
- elif path.endswith('.html'): |
- content = template_renderer.Render( |
- template_cache.GetFromFile( |
- '%s/%s' % (PUBLIC_TEMPLATE_PATH, path)).Get(), |
- self._request) |
- content_type = 'text/html' |
- else: |
- content = None |
+ return self._GetSuccessResponse(path, server_instance) |
except FileNotFoundError: |
- content = None |
- |
- headers = {'x-frame-options': 'sameorigin'} |
- if content is None: |
- def render_template_or_none(path): |
+ # Maybe it didn't find the file because its canonical location is |
+ # somewhere else; this is distinct from "redirects", which are typically |
+ # explicit. This is implicit. |
+ canonical_result = server_instance.path_canonicalizer.Canonicalize(path) |
+ redirect = canonical_result.path.lstrip('/') |
+ if path != redirect: |
+ return Response.Redirect('/' + redirect, |
+ permanent=canonical_result.permanent) |
+ |
+ # Not found for reals. Find the closest 404.html file and serve that; |
+ # e.g. if the path is extensions/manifest/typo.html then first look for |
+ # extensions/manifest/404.html, then extensions/404.html, then 404.html. |
+ # |
+ # Failing that just print 'Not Found' but that should preferrably never |
+ # happen, because it would look really bad. |
+ path_components = path.split('/') |
+ for i in xrange(len(path_components) - 1, -1, -1): |
try: |
- return template_renderer.Render( |
- template_cache.GetFromFile( |
- '%s/%s' % (PUBLIC_TEMPLATE_PATH, path)).Get(), |
- self._request) |
- except FileNotFoundError: |
- return None |
- content = (render_template_or_none('%s/404.html' % |
- path.split('/', 1)[0]) or |
- render_template_or_none('extensions/404.html') or |
- 'Not found') |
- return Response.NotFound(content, headers=headers) |
- |
- if not content: |
+ path_404 = posixpath.join(*(path_components[0:i] + ['404.html'])) |
+ response = self._GetSuccessResponse(path_404, server_instance) |
+ return Response.NotFound(response.content.ToString(), |
+ headers=response.headers) |
+ except FileNotFoundError: continue |
+ logging.error('No 404.html found in %s' % path) |
+ return Response.NotFound('Not Found', headers=_MakeHeaders('text/plain')) |
+ |
+ def _GetSuccessResponse(self, path, server_instance): |
+ '''Returns the Response from trying to render |path| with |
+ |server_instance|. If |path| isn't found then a FileNotFoundError will be |
+ raised, such that the only responses that will be returned from this method |
+ are Ok and Redirect. |
+ ''' |
+ content_provider, path = ( |
+ server_instance.content_providers.GetByServeFrom(path)) |
+ assert content_provider, 'No ContentProvider found for %s' % path |
+ |
+ redirect = Redirector( |
+ server_instance.compiled_fs_factory, |
+ content_provider.file_system).Redirect(self._request.host, path) |
+ if redirect is not None: |
+ return Response.Redirect(redirect, permanent=False) |
+ |
+ content_and_type = content_provider.GetContentAndType( |
+ self._request.host, path).Get() |
+ if not content_and_type.content: |
logging.error('%s had empty content' % path) |
- headers.update({ |
- 'content-type': content_type, |
- 'cache-control': 'max-age=300', |
- }) |
- return Response.Ok(content, headers=headers) |
+ if isinstance(content_and_type.content, Handlebar): |
+ content_and_type.content = server_instance.template_renderer.Render( |
+ content_and_type.content, self._request) |
+ |
+ return Response.Ok(content_and_type.content, |
+ headers=_MakeHeaders(content_and_type.content_type)) |