1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 from fnmatch import fnmatch
7 from urlparse import urlparse
9 from appengine_url_fetcher import AppEngineUrlFetcher
10 from caching_file_system import CachingFileSystem
11 from caching_rietveld_patcher import CachingRietveldPatcher
12 from chained_compiled_file_system import ChainedCompiledFileSystem
13 from compiled_file_system import CompiledFileSystem
14 from environment import IsDevServer
15 from host_file_system_provider import HostFileSystemProvider
16 from instance_servlet import InstanceServlet
17 from render_servlet import RenderServlet
18 from rietveld_patcher import RietveldPatcher, RietveldPatcherError
19 from object_store_creator import ObjectStoreCreator
20 from patched_file_system import PatchedFileSystem
21 from server_instance import ServerInstance
22 from servlet import Request, Response, Servlet
26 class _PatchServletDelegate(RenderServlet.Delegate):
27 def __init__(self, issue, delegate):
29 self._delegate = delegate
31 def CreateServerInstance(self):
32 # start_empty=False because a patch can rely on files that are already in
33 # SVN repository but not yet pulled into data store by cron jobs (a typical
34 # example is to add documentation for an existing API).
35 object_store_creator = ObjectStoreCreator(start_empty=False)
37 unpatched_file_system = self._delegate.CreateHostFileSystemProvider(
38 object_store_creator).GetTrunk()
40 rietveld_patcher = CachingRietveldPatcher(
41 RietveldPatcher(svn_constants.EXTENSIONS_PATH,
43 AppEngineUrlFetcher(url_constants.CODEREVIEW_SERVER)),
46 patched_file_system = PatchedFileSystem(unpatched_file_system,
49 patched_host_file_system_provider = (
50 self._delegate.CreateHostFileSystemProvider(
52 # The patched file system needs to be online otherwise it'd be
53 # impossible to add files in the patches.
55 # The trunk file system for this creator should be the patched one.
56 default_trunk_instance=patched_file_system))
58 combined_compiled_fs_factory = ChainedCompiledFileSystem.Factory(
59 [unpatched_file_system], object_store_creator)
61 branch_utility = self._delegate.CreateBranchUtility(object_store_creator)
63 return ServerInstance(object_store_creator,
64 combined_compiled_fs_factory,
66 patched_host_file_system_provider,
67 self._delegate.CreateGithubFileSystemProvider(
68 object_store_creator),
69 base_path='/_patch/%s/' % self._issue)
71 class PatchServlet(Servlet):
72 '''Servlet which renders patched docs.
74 def __init__(self, request, delegate=None):
75 self._request = request
76 self._delegate = delegate or InstanceServlet.Delegate()
79 if (not IsDevServer() and
80 not fnmatch(urlparse(self._request.host).netloc, '*.appspot.com')):
81 # Only allow patches on appspot URLs; it doesn't matter if appspot.com is
82 # XSS'ed, but it matters for chrome.com.
83 redirect_host = 'https://chrome-apps-doc.appspot.com'
84 logging.info('Redirecting from XSS-able host %s to %s' % (
85 self._request.host, redirect_host))
86 return Response.Redirect(
87 '%s/_patch/%s' % (redirect_host, self._request.path))
89 path_with_issue = self._request.path.lstrip('/')
90 if '/' in path_with_issue:
91 issue, path_without_issue = path_with_issue.split('/', 1)
93 return Response.NotFound('Malformed URL. It should look like ' +
94 'https://developer.chrome.com/_patch/12345/extensions/...')
97 response = RenderServlet(
98 Request(path_without_issue,
100 self._request.headers),
101 _PatchServletDelegate(issue, self._delegate)).Get()
102 # Disable cache for patched content.
103 response.headers.pop('cache-control', None)
104 except RietveldPatcherError as e:
105 response = Response.NotFound(e.message, {'Content-Type': 'text/plain'})
107 redirect_url, permanent = response.GetRedirect()
108 if redirect_url is not None:
109 response = Response.Redirect('/_patch/%s%s' % (issue, redirect_url),