| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import logging |
| 7 import os |
| 6 import sys | 8 import sys |
| 7 import os | |
| 8 | 9 |
| 9 # Add the original server location to sys.path so we are able to import | 10 # Add the original server location to sys.path so we are able to import |
| 10 # modules from there. | 11 # modules from there. |
| 11 SERVER_PATH = 'chrome/common/extensions/docs/server2' | 12 SERVER_PATH = 'chrome/common/extensions/docs/server2' |
| 12 if os.path.abspath(SERVER_PATH) not in sys.path: | 13 if os.path.abspath(SERVER_PATH) not in sys.path: |
| 13 sys.path.append(os.path.abspath(SERVER_PATH)) | 14 sys.path.append(os.path.abspath(SERVER_PATH)) |
| 14 | 15 |
| 15 import branch_utility | |
| 16 import urlfetch | |
| 17 | |
| 18 from google.appengine.ext import webapp | 16 from google.appengine.ext import webapp |
| 17 from google.appengine.api import memcache |
| 19 from google.appengine.ext.webapp.util import run_wsgi_app | 18 from google.appengine.ext.webapp.util import run_wsgi_app |
| 20 | 19 |
| 21 from api_data_source import APIDataSource | 20 from api_data_source import APIDataSource |
| 22 from fetcher_cache import FetcherCache | 21 from appengine_memcache import AppEngineMemcache |
| 22 from branch_utility import BranchUtility |
| 23 from example_zipper import ExampleZipper |
| 24 from file_system_cache import FileSystemCache |
| 23 from intro_data_source import IntroDataSource | 25 from intro_data_source import IntroDataSource |
| 24 from local_fetcher import LocalFetcher | 26 from local_file_system import LocalFileSystem |
| 27 from memcache_file_system import MemcacheFileSystem |
| 25 from samples_data_source import SamplesDataSource | 28 from samples_data_source import SamplesDataSource |
| 26 from server_instance import ServerInstance | 29 from server_instance import ServerInstance |
| 27 from subversion_fetcher import SubversionFetcher | 30 from subversion_file_system import SubversionFileSystem |
| 28 from template_data_source import TemplateDataSource | 31 from template_data_source import TemplateDataSource |
| 29 from example_zipper import ExampleZipper | 32 from appengine_url_fetcher import AppEngineUrlFetcher |
| 33 |
| 34 SVN_URL = 'http://src.chromium.org/chrome' |
| 35 TRUNK_URL = SVN_URL + '/trunk' |
| 36 BRANCH_URL = SVN_URL + '/branches' |
| 30 | 37 |
| 31 EXTENSIONS_PATH = 'chrome/common/extensions' | 38 EXTENSIONS_PATH = 'chrome/common/extensions' |
| 32 DOCS_PATH = 'docs' | 39 DOCS_PATH = 'docs' |
| 33 API_PATH = 'api' | 40 API_PATH = 'api' |
| 34 INTRO_PATH = DOCS_PATH + '/server2/templates/intros' | 41 INTRO_PATH = DOCS_PATH + '/server2/templates/intros' |
| 35 ARTICLE_PATH = DOCS_PATH + '/server2/templates/articles' | 42 ARTICLE_PATH = DOCS_PATH + '/server2/templates/articles' |
| 36 PUBLIC_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/public' | 43 PUBLIC_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/public' |
| 37 PRIVATE_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/private' | 44 PRIVATE_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/private' |
| 38 EXAMPLES_PATH = 'examples' | 45 EXAMPLES_PATH = DOCS_PATH + '/examples' |
| 39 FULL_EXAMPLES_PATH = DOCS_PATH + '/' + EXAMPLES_PATH | 46 FULL_EXAMPLES_PATH = DOCS_PATH + '/' + EXAMPLES_PATH |
| 40 | 47 |
| 41 # The branch that the server will default to when no branch is specified in the | 48 # The branch that the server will default to when no branch is specified in the |
| 42 # URL. This is necessary because it is not possible to pass flags to the script | 49 # URL. This is necessary because it is not possible to pass flags to the script |
| 43 # handler. | 50 # handler. |
| 44 DEFAULT_BRANCH = 'local' | 51 DEFAULT_BRANCH = 'local' |
| 45 | 52 |
| 46 # Global cache of instances because the Server is recreated for every request. | 53 # Global cache of instances because the Server is recreated for every request. |
| 47 SERVER_INSTANCES = {} | 54 SERVER_INSTANCES = {} |
| 48 | 55 |
| 56 OMAHA_PROXY_URL = 'http://omahaproxy.appspot.com/json' |
| 57 BRANCH_UTILITY = BranchUtility(OMAHA_PROXY_URL, |
| 58 DEFAULT_BRANCH, |
| 59 AppEngineUrlFetcher(''), |
| 60 AppEngineMemcache('branch_utility', memcache)) |
| 61 |
| 62 def _GetURLFromBranch(branch): |
| 63 if branch == 'trunk': |
| 64 return TRUNK_URL + '/src' |
| 65 return BRANCH_URL + '/' + branch + '/src' |
| 66 |
| 49 class Server(webapp.RequestHandler): | 67 class Server(webapp.RequestHandler): |
| 50 def _GetInstanceForBranch(self, branch): | 68 def _GetInstanceForBranch(self, branch): |
| 51 if branch in SERVER_INSTANCES: | 69 if branch in SERVER_INSTANCES: |
| 52 return SERVER_INSTANCES[branch] | 70 return SERVER_INSTANCES[branch] |
| 53 if branch == 'local': | 71 if branch == 'local': |
| 54 fetcher = LocalFetcher(EXTENSIONS_PATH) | 72 file_system = LocalFileSystem(EXTENSIONS_PATH) |
| 55 # No cache for local doc server. | |
| 56 cache_timeout_seconds = 0 | |
| 57 else: | 73 else: |
| 58 fetcher = SubversionFetcher(branch, EXTENSIONS_PATH, urlfetch) | 74 fetcher = AppEngineUrlFetcher( |
| 59 cache_timeout_seconds = 300 | 75 _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH) |
| 60 cache_builder = FetcherCache.Builder(fetcher, cache_timeout_seconds) | 76 file_system = MemcacheFileSystem(SubversionFileSystem(fetcher), |
| 77 AppEngineMemcache(branch, memcache)) |
| 78 |
| 79 cache_builder = FileSystemCache.Builder(file_system) |
| 61 api_data_source = APIDataSource(cache_builder, API_PATH) | 80 api_data_source = APIDataSource(cache_builder, API_PATH) |
| 62 intro_data_source = IntroDataSource(cache_builder, | 81 intro_data_source = IntroDataSource(cache_builder, |
| 63 [INTRO_PATH, ARTICLE_PATH]) | 82 [INTRO_PATH, ARTICLE_PATH]) |
| 64 samples_data_source = SamplesDataSource(fetcher, | 83 samples_data_source = SamplesDataSource(file_system, |
| 65 cache_builder, | 84 cache_builder, |
| 66 EXAMPLES_PATH) | 85 EXAMPLES_PATH) |
| 67 template_data_source = TemplateDataSource( | 86 template_data_source = TemplateDataSource( |
| 68 branch, | 87 branch, |
| 69 api_data_source, | 88 api_data_source, |
| 70 intro_data_source, | 89 intro_data_source, |
| 71 samples_data_source, | 90 samples_data_source, |
| 72 cache_builder, | 91 cache_builder, |
| 73 [PUBLIC_TEMPLATE_PATH, PRIVATE_TEMPLATE_PATH]) | 92 [PUBLIC_TEMPLATE_PATH, PRIVATE_TEMPLATE_PATH]) |
| 74 example_zipper = ExampleZipper(fetcher, | 93 example_zipper = ExampleZipper(file_system, |
| 75 cache_builder, | 94 cache_builder, |
| 76 DOCS_PATH, | 95 DOCS_PATH, |
| 77 EXAMPLES_PATH) | 96 EXAMPLES_PATH) |
| 78 SERVER_INSTANCES[branch] = ServerInstance( | 97 SERVER_INSTANCES[branch] = ServerInstance( |
| 79 template_data_source, | 98 template_data_source, |
| 80 example_zipper, | 99 example_zipper, |
| 81 cache_builder) | 100 cache_builder) |
| 82 return SERVER_INSTANCES[branch] | 101 return SERVER_INSTANCES[branch] |
| 83 | 102 |
| 84 def _HandleRequest(self, path): | 103 def _HandleRequest(self, path): |
| 85 channel_name, real_path = ( | 104 channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path) |
| 86 branch_utility.SplitChannelNameFromPath(path, default=DEFAULT_BRANCH)) | 105 branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name) |
| 87 branch = branch_utility.GetBranchNumberForChannelName(channel_name, | |
| 88 urlfetch) | |
| 89 if real_path == '': | 106 if real_path == '': |
| 90 real_path = 'index.html' | 107 real_path = 'index.html' |
| 108 # TODO: This leaks Server instances when branch bumps. |
| 91 self._GetInstanceForBranch(branch).Get(real_path, self) | 109 self._GetInstanceForBranch(branch).Get(real_path, self) |
| 92 | 110 |
| 93 def get(self): | 111 def get(self): |
| 94 path = self.request.path | 112 path = self.request.path |
| 113 if '_ah/warmup' in path: |
| 114 logging.info('Warmup request.') |
| 115 self.get('/chrome/extensions/trunk/samples.html') |
| 116 self.get('/chrome/extensions/dev/samples.html') |
| 117 self.get('/chrome/extensions/beta/samples.html') |
| 118 self.get('/chrome/extensions/stable/samples.html') |
| 119 return |
| 95 # Redirect paths like "directory" to "directory/". This is so relative file | 120 # Redirect paths like "directory" to "directory/". This is so relative file |
| 96 # paths will know to treat this as a directory. | 121 # paths will know to treat this as a directory. |
| 97 if os.path.splitext(path)[1] == '' and path[-1] != '/': | 122 if os.path.splitext(path)[1] == '' and path[-1] != '/': |
| 98 self.redirect(path + '/') | 123 self.redirect(path + '/') |
| 99 path = path.replace('/chrome/extensions/', '') | 124 path = path.replace('/chrome/extensions/', '') |
| 100 path = path.strip('/') | 125 path = path.strip('/') |
| 101 self._HandleRequest(path) | 126 self._HandleRequest(path) |
| 102 | 127 |
| 103 def main(): | 128 def main(): |
| 104 handlers = [ | 129 handlers = [ |
| 105 ('/.*', Server), | 130 ('/.*', Server), |
| 106 ] | 131 ] |
| 107 run_wsgi_app(webapp.WSGIApplication(handlers, debug=False)) | 132 run_wsgi_app(webapp.WSGIApplication(handlers, debug=False)) |
| 108 | 133 |
| 109 | 134 |
| 110 if __name__ == '__main__': | 135 if __name__ == '__main__': |
| 111 main() | 136 main() |
| OLD | NEW |