Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: chrome/common/extensions/docs/server2/handler.py

Issue 14273035: Docserver: refactor the Handler/ServerInstance relationship into a servlet (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 from appengine_wrappers import webapp
6 import os 6 from cron_servlet import CronServlet
7 from StringIO import StringIO 7 from render_servlet import RenderServlet
8 import traceback
9 8
10 from appengine_wrappers import ( 9 _SERVLETS = {
11 DeadlineExceededError, IsDevServer, logservice, memcache, urlfetch, webapp) 10 'cron': CronServlet,
12 from branch_utility import BranchUtility 11 }
13 from server_instance import ServerInstance
14 import svn_constants
15 import time
16
17 # The default channel to serve docs for if no channel is specified.
18 _DEFAULT_CHANNEL = 'stable'
19 12
20 class Handler(webapp.RequestHandler): 13 class Handler(webapp.RequestHandler):
21 # 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 # which is as far as instances look.
24 #
25 # Why? SVN is slow and a bit flaky. Cronjobs failing is annoying but
26 # temporary. Instances failing affects users, and is really bad.
27 #
28 # 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 # 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 # round trip to SVN.
33 #
34 # 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 # TODO(kalman): achieve this via proper dependency injection.
37 ALWAYS_ONLINE = IsDevServer()
38
39 def __init__(self, request, response): 14 def __init__(self, request, response):
40 super(Handler, self).__init__(request, response) 15 super(Handler, self).__init__(request, response)
41 16
42 def _HandleGet(self, path):
43 channel_name, real_path = BranchUtility.SplitChannelNameFromPath(path)
44
45 if channel_name == _DEFAULT_CHANNEL:
46 self.redirect('/%s' % real_path)
47 return
48
49 if channel_name is None:
50 channel_name = _DEFAULT_CHANNEL
51
52 # TODO(kalman): Check if |path| is a directory and serve path/index.html
53 # rather than special-casing apps/extensions.
54 if real_path.strip('/') == 'apps':
55 real_path = 'apps/index.html'
56 if real_path.strip('/') == 'extensions':
57 real_path = 'extensions/index.html'
58
59 constructor = (
60 ServerInstance.CreateOnline if Handler.ALWAYS_ONLINE else
61 ServerInstance.GetOrCreateOffline)
62 server_instance = constructor(channel_name)
63
64 canonical_path = server_instance.path_canonicalizer.Canonicalize(real_path)
65 if real_path != canonical_path:
66 self.redirect(canonical_path)
67 return
68
69 server_instance.Get(real_path, self.request, self.response)
70
71 def _HandleCron(self, path):
72 # Cron strategy:
73 #
74 # Find all public template files and static files, and render them. Most of
75 # the time these won't have changed since the last cron run, so it's a
76 # little wasteful, but hopefully rendering is really fast (if it isn't we
77 # have a problem).
78 class MockResponse(object):
79 def __init__(self):
80 self.status = 200
81 self.out = StringIO()
82 self.headers = {}
83 def set_status(self, status):
84 self.status = status
85 def clear(self, *args):
86 pass
87
88 class MockRequest(object):
89 def __init__(self, path):
90 self.headers = {}
91 self.path = path
92 self.url = '//localhost/%s' % path
93
94 channel = path.split('/')[-1]
95 logging.info('cron/%s: starting' % channel)
96
97 server_instance = ServerInstance.CreateOnline(channel)
98
99 def run_cron_for_dir(d, path_prefix=''):
100 success = True
101 start_time = time.time()
102 files = [f for f in server_instance.content_cache.GetFromFileListing(d)
103 if not f.endswith('/')]
104 logging.info('cron/%s: rendering %s files from %s...' % (
105 channel, len(files), d))
106 for i, f in enumerate(files):
107 error = None
108 path = '%s%s' % (path_prefix, f)
109 try:
110 response = MockResponse()
111 server_instance.Get(path, MockRequest(path), response)
112 if response.status != 200:
113 error = 'Got %s response' % response.status
114 except DeadlineExceededError:
115 logging.error(
116 'cron/%s: deadline exceeded rendering %s (%s of %s): %s' % (
117 channel, path, i + 1, len(files), traceback.format_exc()))
118 raise
119 except error:
120 pass
121 if error:
122 logging.error('cron/%s: error rendering %s: %s' % (
123 channel, path, error))
124 success = False
125 logging.info('cron/%s: rendering %s files from %s took %s seconds' % (
126 channel, len(files), d, time.time() - start_time))
127 return success
128
129 success = True
130 for path, path_prefix in (
131 # Note: rendering the public templates will pull in all of the private
132 # templates.
133 (svn_constants.PUBLIC_TEMPLATE_PATH, ''),
134 # Note: rendering the public templates will have pulled in the .js and
135 # manifest.json files (for listing examples on the API reference pages),
136 # but there are still images, CSS, etc.
137 (svn_constants.STATIC_PATH, 'static/'),
138 (svn_constants.EXAMPLES_PATH, 'extensions/examples/')):
139 try:
140 # 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.
142 success = run_cron_for_dir(path, path_prefix=path_prefix) and success
143 except DeadlineExceededError:
144 success = False
145 break
146
147 if success:
148 self.response.status = 200
149 self.response.out.write('Success')
150 else:
151 self.response.status = 500
152 self.response.out.write('Failure')
153
154 logging.info('cron/%s: finished' % channel)
155
156 def _RedirectSpecialCases(self, path): 17 def _RedirectSpecialCases(self, path):
157 google_dev_url = 'http://developer.google.com/chrome' 18 if not path or path == 'index.html':
158 if path == '/' or path == '/index.html': 19 self.redirect('http://developer.google.com/chrome')
159 self.redirect(google_dev_url)
160 return True 20 return True
161 21
162 if path == '/apps.html': 22 if path == 'apps.html':
163 self.redirect('/apps/about_apps.html') 23 self.redirect('/apps/about_apps.html')
164 return True 24 return True
165 25
166 return False 26 return False
167 27
168 def _RedirectFromCodeDotGoogleDotCom(self, path): 28 def _RedirectFromCodeDotGoogleDotCom(self, path):
169 if (not self.request.url.startswith(('http://code.google.com', 29 if (not self.request.url.startswith(('http://code.google.com',
170 'https://code.google.com'))): 30 'https://code.google.com'))):
171 return False 31 return False
172 32
173 new_url = 'http://developer.chrome.com/' 33 new_url = 'http://developer.chrome.com/'
174 34
175 # switch to https if necessary 35 # switch to https if necessary
176 if (self.request.url.startswith('https')): 36 if (self.request.url.startswith('https')):
177 new_url = new_url.replace('http', 'https', 1) 37 new_url = new_url.replace('http', 'https', 1)
178 38
179 path = path.split('/') 39 path = path.split('/')
180 if len(path) > 0 and path[0] == 'chrome': 40 if len(path) > 0 and path[0] == 'chrome':
181 path.pop(0) 41 path.pop(0)
182 for channel in BranchUtility.GetAllBranchNames(): 42 for channel in BranchUtility.GetAllBranchNames():
183 if channel in path: 43 if channel in path:
184 position = path.index(channel) 44 position = path.index(channel)
185 path.pop(position) 45 path.pop(position)
186 path.insert(0, channel) 46 path.insert(0, channel)
187 new_url += '/'.join(path) 47 new_url += '/'.join(path)
188 self.redirect(new_url) 48 self.redirect(new_url)
189 return True 49 return True
190 50
191 def get(self): 51 def get(self):
192 path = self.request.path 52 path, request, response = (self.request.path.lstrip('/'),
53 self.request,
54 self.response)
193 55
194 if path in ['favicon.ico', 'robots.txt']: 56 if path in ['favicon.ico', 'robots.txt']:
195 response.set_status(404) 57 response.set_status(404)
196 return 58 return
197 59
198 if self._RedirectSpecialCases(path): 60 if self._RedirectSpecialCases(path):
199 return 61 return
200
201 if path.startswith('/cron'):
202 # 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.
204 logservice.AUTOFLUSH_ENABLED = False
205 try:
206 self._HandleCron(path)
207 finally:
208 logservice.flush()
209 return
210
211 # Redirect paths like "directory" to "directory/". This is so relative
212 # file paths will know to treat this as a directory.
213 if os.path.splitext(path)[1] == '' and path[-1] != '/':
214 self.redirect(path + '/')
215 return
216
217 path = path.strip('/')
218 if self._RedirectFromCodeDotGoogleDotCom(path): 62 if self._RedirectFromCodeDotGoogleDotCom(path):
219 return 63 return
220 64
221 self._HandleGet(path) 65 if path.startswith('_'):
66 servlet_path = path[1:]
67 if servlet_path.find('/') == -1:
68 servlet_path += '/'
69 servlet_name, servlet_path = servlet_path.split('/', 1)
70 servlet_class = _SERVLETS.get(servlet_name)
71 if servlet_class is None:
72 response.out.write('"%s" servlet not found' % servlet_path)
73 response.set_status(404)
74 return
75 else:
76 servlet_path = path
77 servlet_class = RenderServlet
78
79 servlet_class(servlet_path, request, response).Get()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698