| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 import os | 6 import os |
| 7 from StringIO import StringIO | 7 from StringIO import StringIO |
| 8 import traceback | 8 import traceback |
| 9 | 9 |
| 10 from appengine_wrappers import ( | 10 from appengine_wrappers import ( |
| 11 DeadlineExceededError, IsDevServer, logservice, memcache, urlfetch, webapp) | 11 DeadlineExceededError, IsDevServer, logservice, memcache, urlfetch, webapp) |
| 12 from branch_utility import BranchUtility | 12 from branch_utility import BranchUtility |
| 13 from patch_servlet import PatchServlet |
| 13 from server_instance import ServerInstance | 14 from server_instance import ServerInstance |
| 14 import svn_constants | 15 import svn_constants |
| 15 import time | 16 import time |
| 16 | 17 |
| 17 # The default channel to serve docs for if no channel is specified. | 18 # The default channel to serve docs for if no channel is specified. |
| 18 _DEFAULT_CHANNEL = 'stable' | 19 _DEFAULT_CHANNEL = 'stable' |
| 19 | 20 |
| 20 class Handler(webapp.RequestHandler): | 21 class Handler(webapp.RequestHandler): |
| 21 # AppEngine instances should never need to call out to SVN. That should only | 22 # AppEngine instances should never need to call out to SVN. That should only |
| 22 # ever be done by the cronjobs, which then write the result into DataStore, | 23 # ever be done by the cronjobs, which then write the result into DataStore, |
| 23 # which is as far as instances look. | 24 # which is as far as instances look. |
| 24 # | 25 # |
| 25 # Why? SVN is slow and a bit flaky. Cronjobs failing is annoying but | 26 # Why? SVN is slow and a bit flaky. Cronjobs failing is annoying but |
| 26 # temporary. Instances failing affects users, and is really bad. | 27 # temporary. Instances failing affects users, and is really bad. |
| 27 # | 28 # |
| 28 # Anyway - to enforce this, we actually don't give instances access to SVN. | 29 # Anyway - to enforce this, we actually don't give instances access to SVN. |
| 29 # If anything is missing from datastore, it'll be a 404. If the cronjobs | 30 # If anything is missing from datastore, it'll be a 404. If the cronjobs |
| 30 # don't manage to catch everything - uhoh. On the other hand, we'll figure it | 31 # don't manage to catch everything - uhoh. On the other hand, we'll figure it |
| 31 # out pretty soon, and it also means that legitimate 404s are caught before a | 32 # out pretty soon, and it also means that legitimate 404s are caught before a |
| 32 # round trip to SVN. | 33 # round trip to SVN. |
| 33 # | 34 # |
| 34 # However, we can't expect users of preview.py nor the dev server to run a | 35 # However, we can't expect users of preview.py nor the dev server to run a |
| 35 # cronjob first, so, this is a hack allow that to be online all of the time. | 36 # cronjob first, so, this is a hack allow that to be online all of the time. |
| 36 # TODO(kalman): achieve this via proper dependency injection. | 37 # TODO(kalman): achieve this via proper dependency injection. |
| 37 ALWAYS_ONLINE = IsDevServer() | 38 ALWAYS_ONLINE = True#IsDevServer() |
| 38 | 39 |
| 39 def __init__(self, request, response): | 40 def __init__(self, request, response): |
| 40 super(Handler, self).__init__(request, response) | 41 super(Handler, self).__init__(request, response) |
| 41 | 42 |
| 42 def _HandleGet(self, path): | 43 def _HandleGet(self, path): |
| 43 channel_name, real_path = BranchUtility.SplitChannelNameFromPath(path) | 44 channel_name, issue, real_path = ( |
| 45 PatchServlet.SplitPatchFromPath(path)) |
| 46 if issue is None: |
| 47 channel_name, real_path = BranchUtility.SplitChannelNameFromPath(path) |
| 44 | 48 |
| 45 if channel_name == _DEFAULT_CHANNEL: | 49 if channel_name == _DEFAULT_CHANNEL: |
| 46 self.redirect('/%s' % real_path) | 50 self.redirect('/%s' % real_path) |
| 47 return | 51 return |
| 48 | 52 |
| 49 if channel_name is None: | 53 if channel_name is None: |
| 50 channel_name = _DEFAULT_CHANNEL | 54 channel_name = _DEFAULT_CHANNEL |
| 51 | 55 |
| 56 branch_path = path.split(real_path, 1)[0].rstrip('/') |
| 57 if branch_path != '': |
| 58 branch_path = '/' + branch_path |
| 59 |
| 52 # TODO(kalman): Check if |path| is a directory and serve path/index.html | 60 # TODO(kalman): Check if |path| is a directory and serve path/index.html |
| 53 # rather than special-casing apps/extensions. | 61 # rather than special-casing apps/extensions. |
| 54 if real_path.strip('/') == 'apps': | 62 if real_path.strip('/') == 'apps': |
| 55 real_path = 'apps/index.html' | 63 real_path = 'apps/index.html' |
| 56 if real_path.strip('/') == 'extensions': | 64 if real_path.strip('/') == 'extensions': |
| 57 real_path = 'extensions/index.html' | 65 real_path = 'extensions/index.html' |
| 58 | 66 |
| 59 constructor = ( | 67 # Serving patched content requires an online server instance to fetch |
| 60 ServerInstance.CreateOnline if Handler.ALWAYS_ONLINE else | 68 # data from SVN and Rietveld. |
| 61 ServerInstance.GetOrCreateOffline) | 69 if Handler.ALWAYS_ONLINE or issue is not None: |
| 62 server_instance = constructor(channel_name) | 70 server_instance = ServerInstance.CreateOnline(channel_name, |
| 71 branch_path, |
| 72 issue) |
| 73 else: |
| 74 server_instance = ServerInstance.GetOrCreateOffline(channel_name, |
| 75 branch_path) |
| 63 | 76 |
| 64 canonical_path = server_instance.path_canonicalizer.Canonicalize(real_path) | 77 canonical_path = server_instance.path_canonicalizer.Canonicalize(real_path) |
| 65 if real_path != canonical_path: | 78 if real_path != canonical_path: |
| 66 self.redirect(canonical_path) | 79 self.redirect(canonical_path) |
| 67 return | 80 return |
| 68 | 81 |
| 69 server_instance.Get(real_path, self.request, self.response) | 82 server_instance.Get(real_path, self.request, self.response) |
| 70 | 83 |
| 71 def _HandleCron(self, path): | 84 def _HandleCron(self, path): |
| 72 # Cron strategy: | 85 # Cron strategy: |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 | 141 |
| 129 success = True | 142 success = True |
| 130 for path, path_prefix in ( | 143 for path, path_prefix in ( |
| 131 # Note: rendering the public templates will pull in all of the private | 144 # Note: rendering the public templates will pull in all of the private |
| 132 # templates. | 145 # templates. |
| 133 (svn_constants.PUBLIC_TEMPLATE_PATH, ''), | 146 (svn_constants.PUBLIC_TEMPLATE_PATH, ''), |
| 134 # Note: rendering the public templates will have pulled in the .js and | 147 # Note: rendering the public templates will have pulled in the .js and |
| 135 # manifest.json files (for listing examples on the API reference pages), | 148 # manifest.json files (for listing examples on the API reference pages), |
| 136 # but there are still images, CSS, etc. | 149 # but there are still images, CSS, etc. |
| 137 (svn_constants.STATIC_PATH, 'static/'), | 150 (svn_constants.STATIC_PATH, 'static/'), |
| 138 (svn_constants.EXAMPLES_PATH, 'extensions/examples/')): | 151 #(svn_constants.EXAMPLES_PATH, 'extensions/examples/') |
| 152 ): |
| 139 try: | 153 try: |
| 140 # Note: don't try to short circuit any of this stuff. We want to run | 154 # Note: don't try to short circuit any of this stuff. We want to run |
| 141 # the cron for all the directories regardless of intermediate failures. | 155 # the cron for all the directories regardless of intermediate failures. |
| 142 success = run_cron_for_dir(path, path_prefix=path_prefix) and success | 156 success = run_cron_for_dir(path, path_prefix=path_prefix) and success |
| 143 except DeadlineExceededError: | 157 except DeadlineExceededError: |
| 144 success = False | 158 success = False |
| 145 break | 159 break |
| 146 | 160 |
| 147 if success: | 161 if success: |
| 148 self.response.status = 200 | 162 self.response.status = 200 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 position = path.index(channel) | 198 position = path.index(channel) |
| 185 path.pop(position) | 199 path.pop(position) |
| 186 path.insert(0, channel) | 200 path.insert(0, channel) |
| 187 new_url += '/'.join(path) | 201 new_url += '/'.join(path) |
| 188 self.redirect(new_url) | 202 self.redirect(new_url) |
| 189 return True | 203 return True |
| 190 | 204 |
| 191 def get(self): | 205 def get(self): |
| 192 path = self.request.path | 206 path = self.request.path |
| 193 | 207 |
| 194 if path in ['favicon.ico', 'robots.txt']: | |
| 195 response.set_status(404) | |
| 196 return | |
| 197 | |
| 198 if self._RedirectSpecialCases(path): | 208 if self._RedirectSpecialCases(path): |
| 199 return | 209 return |
| 200 | 210 |
| 201 if path.startswith('/cron'): | 211 if path.startswith('/cron'): |
| 202 # Crons often time out, and when they do *and* then eventually try to | 212 # Crons often time out, and when they do *and* then eventually try to |
| 203 # flush logs they die. Turn off autoflush and manually do so at the end. | 213 # flush logs they die. Turn off autoflush and manually do so at the end. |
| 204 logservice.AUTOFLUSH_ENABLED = False | 214 logservice.AUTOFLUSH_ENABLED = False |
| 205 try: | 215 try: |
| 206 self._HandleCron(path) | 216 self._HandleCron(path) |
| 207 finally: | 217 finally: |
| 208 logservice.flush() | 218 logservice.flush() |
| 209 return | 219 return |
| 210 | 220 |
| 211 # Redirect paths like "directory" to "directory/". This is so relative | 221 # Redirect paths like "directory" to "directory/". This is so relative |
| 212 # file paths will know to treat this as a directory. | 222 # file paths will know to treat this as a directory. |
| 213 if os.path.splitext(path)[1] == '' and path[-1] != '/': | 223 if os.path.splitext(path)[1] == '' and path[-1] != '/': |
| 214 self.redirect(path + '/') | 224 self.redirect(path + '/') |
| 215 return | 225 return |
| 216 | 226 |
| 217 path = path.strip('/') | 227 path = path.strip('/') |
| 218 if self._RedirectFromCodeDotGoogleDotCom(path): | 228 if self._RedirectFromCodeDotGoogleDotCom(path): |
| 219 return | 229 return |
| 220 | 230 |
| 221 self._HandleGet(path) | 231 self._HandleGet(path) |
| OLD | NEW |