4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> |
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> |
5 # |
5 # |
6 # This software may be used and distributed according to the terms of the |
6 # This software may be used and distributed according to the terms of the |
7 # GNU General Public License version 2 or any later version. |
7 # GNU General Public License version 2 or any later version. |
8 |
8 |
|
9 import contextlib |
9 import os |
10 import os |
10 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview |
11 from mercurial import ui, hg, hook, error, encoding, templater, util, repoview |
11 from mercurial.templatefilters import websub |
12 from mercurial.templatefilters import websub |
12 from common import get_stat, ErrorResponse, permhooks, caching |
13 from common import get_stat, ErrorResponse, permhooks, caching |
13 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST |
14 from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST |
206 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb') |
207 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb') |
207 # displaying bundling progress bar while serving feel wrong and may |
208 # displaying bundling progress bar while serving feel wrong and may |
208 # break some wsgi implementation. |
209 # break some wsgi implementation. |
209 r.ui.setconfig('progress', 'disable', 'true', 'hgweb') |
210 r.ui.setconfig('progress', 'disable', 'true', 'hgweb') |
210 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb') |
211 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb') |
211 self._repo = hg.cachedlocalrepo(self._webifyrepo(r)) |
212 self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))] |
|
213 self._lastrepo = self._repos[0] |
212 hook.redirect(True) |
214 hook.redirect(True) |
213 self.reponame = name |
215 self.reponame = name |
214 |
216 |
215 def _webifyrepo(self, repo): |
217 def _webifyrepo(self, repo): |
216 repo = getwebview(repo) |
218 repo = getwebview(repo) |
217 self.websubtable = webutil.getwebsubs(repo) |
219 self.websubtable = webutil.getwebsubs(repo) |
218 return repo |
220 return repo |
219 |
221 |
220 def _getrepo(self): |
222 @contextlib.contextmanager |
221 r, created = self._repo.fetch() |
223 def _obtainrepo(self): |
222 if created: |
224 """Obtain a repo unique to the caller. |
223 r = self._webifyrepo(r) |
225 |
224 |
226 Internally we maintain a stack of cachedlocalrepo instances |
225 self.mtime = self._repo.mtime |
227 to be handed out. If one is available, we pop it and return it, |
226 return r |
228 ensuring it is up to date in the process. If one is not available, |
|
229 we clone the most recently used repo instance and return it. |
|
230 |
|
231 It is currently possible for the stack to grow without bounds |
|
232 if the server allows infinite threads. However, servers should |
|
233 have a thread limit, thus establishing our limit. |
|
234 """ |
|
235 if self._repos: |
|
236 cached = self._repos.pop() |
|
237 r, created = cached.fetch() |
|
238 if created: |
|
239 r = self._webifyrepo(r) |
|
240 else: |
|
241 cached = self._lastrepo.copy() |
|
242 r, created = cached.fetch() |
|
243 |
|
244 self._lastrepo = cached |
|
245 self.mtime = cached.mtime |
|
246 try: |
|
247 yield r |
|
248 finally: |
|
249 self._repos.append(cached) |
227 |
250 |
228 def run(self): |
251 def run(self): |
229 """Start a server from CGI environment. |
252 """Start a server from CGI environment. |
230 |
253 |
231 Modern servers should be using WSGI and should avoid this |
254 Modern servers should be using WSGI and should avoid this |
249 """Internal method to run the WSGI application. |
272 """Internal method to run the WSGI application. |
250 |
273 |
251 This is typically only called by Mercurial. External consumers |
274 This is typically only called by Mercurial. External consumers |
252 should be using instances of this class as the WSGI application. |
275 should be using instances of this class as the WSGI application. |
253 """ |
276 """ |
254 repo = self._getrepo() |
277 with self._obtainrepo() as repo: |
|
278 return self._runwsgi(req, repo) |
|
279 |
|
280 def _runwsgi(self, req, repo): |
255 rctx = requestcontext(self, repo) |
281 rctx = requestcontext(self, repo) |
256 |
282 |
257 # This state is global across all threads. |
283 # This state is global across all threads. |
258 encoding.encoding = rctx.config('web', 'encoding', encoding.encoding) |
284 encoding.encoding = rctx.config('web', 'encoding', encoding.encoding) |
259 rctx.repo.ui.environ = req.env |
285 rctx.repo.ui.environ = req.env |