Home | History | Annotate | Download | only in server2
      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.
      4 
      5 from fnmatch import fnmatch
      6 import logging
      7 from urlparse import urlparse
      8 
      9 from appengine_url_fetcher import AppEngineUrlFetcher
     10 from caching_rietveld_patcher import CachingRietveldPatcher
     11 from chained_compiled_file_system import ChainedCompiledFileSystem
     12 from environment import IsDevServer
     13 from extensions_paths import CONTENT_PROVIDERS
     14 from instance_servlet import InstanceServlet
     15 from render_servlet import RenderServlet
     16 from rietveld_patcher import RietveldPatcher, RietveldPatcherError
     17 from object_store_creator import ObjectStoreCreator
     18 from patched_file_system import PatchedFileSystem
     19 from server_instance import ServerInstance
     20 from servlet import Request, Response, Servlet
     21 import url_constants
     22 from gcs_file_system_provider import CloudStorageFileSystemProvider
     23 
     24 
     25 class _PatchServletDelegate(RenderServlet.Delegate):
     26   def __init__(self, issue, delegate):
     27     self._issue = issue
     28     self._delegate = delegate
     29 
     30   def CreateServerInstance(self):
     31     # start_empty=False because a patch can rely on files that are already in
     32     # the Git repository but not yet pulled into data store by cron jobs (a
     33     # typical example is to add documentation for an existing API).
     34     object_store_creator = ObjectStoreCreator(start_empty=False)
     35 
     36     unpatched_file_system = self._delegate.CreateHostFileSystemProvider(
     37         object_store_creator).GetMaster()
     38 
     39     rietveld_patcher = CachingRietveldPatcher(
     40         RietveldPatcher(self._issue,
     41                         AppEngineUrlFetcher(url_constants.CODEREVIEW_SERVER)),
     42         object_store_creator)
     43 
     44     patched_file_system = PatchedFileSystem(unpatched_file_system,
     45                                             rietveld_patcher)
     46 
     47     patched_host_file_system_provider = (
     48         self._delegate.CreateHostFileSystemProvider(
     49             object_store_creator,
     50             # The patched file system needs to be online otherwise it'd be
     51             # impossible to add files in the patches.
     52             offline=False,
     53             # The master file system for this creator should be the patched one.
     54             default_master_instance=patched_file_system))
     55 
     56     combined_compiled_fs_factory = ChainedCompiledFileSystem.Factory(
     57         [unpatched_file_system], object_store_creator)
     58 
     59     branch_utility = self._delegate.CreateBranchUtility(object_store_creator)
     60 
     61     server_instance = ServerInstance(
     62         object_store_creator,
     63         combined_compiled_fs_factory,
     64         branch_utility,
     65         patched_host_file_system_provider,
     66         self._delegate.CreateGithubFileSystemProvider(object_store_creator),
     67         CloudStorageFileSystemProvider(object_store_creator),
     68         base_path='/_patch/%s/' % self._issue)
     69 
     70     # HACK: if content_providers.json changes in this patch then the cron needs
     71     # to be re-run to pull in the new configuration.
     72     _, _, modified = rietveld_patcher.GetPatchedFiles()
     73     if CONTENT_PROVIDERS in modified:
     74       server_instance.content_providers.Refresh().Get()
     75 
     76     return server_instance
     77 
     78 class PatchServlet(Servlet):
     79   '''Servlet which renders patched docs.
     80   '''
     81   def __init__(self, request, delegate=None):
     82     self._request = request
     83     self._delegate = delegate or InstanceServlet.Delegate()
     84 
     85   def Get(self):
     86     if (not IsDevServer() and
     87         not fnmatch(urlparse(self._request.host).netloc, '*.appspot.com')):
     88       # Only allow patches on appspot URLs; it doesn't matter if appspot.com is
     89       # XSS'ed, but it matters for chrome.com.
     90       redirect_host = 'https://chrome-apps-doc.appspot.com'
     91       logging.info('Redirecting from XSS-able host %s to %s' % (
     92           self._request.host, redirect_host))
     93       return Response.Redirect(
     94           '%s/_patch/%s' % (redirect_host, self._request.path))
     95 
     96     path_with_issue = self._request.path.lstrip('/')
     97     if '/' in path_with_issue:
     98       issue, path_without_issue = path_with_issue.split('/', 1)
     99     else:
    100       return Response.NotFound('Malformed URL. It should look like ' +
    101           'https://developer.chrome.com/_patch/12345/extensions/...')
    102 
    103     try:
    104       response = RenderServlet(
    105           Request(path_without_issue,
    106                   self._request.host,
    107                   self._request.headers),
    108           _PatchServletDelegate(issue, self._delegate)).Get()
    109       # Disable cache for patched content.
    110       response.headers.pop('cache-control', None)
    111     except RietveldPatcherError as e:
    112       response = Response.NotFound(e.message, {'Content-Type': 'text/plain'})
    113 
    114     redirect_url, permanent = response.GetRedirect()
    115     if redirect_url is not None:
    116       response = Response.Redirect('/_patch/%s%s' % (issue, redirect_url),
    117                                    permanent)
    118     return response
    119