##// END OF EJS Templates
Mercurial: fix stream ehxaustion problem....
marcink -
r249:8784eaf0 default
parent child Browse files
Show More
@@ -1,209 +1,229 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2017 RodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import os
19 19 import logging
20 import itertools
20 21
21 22 import mercurial
22 23 import mercurial.error
23 24 import mercurial.hgweb.common
24 25 import mercurial.hgweb.hgweb_mod
25 26 import mercurial.hgweb.protocol
26 27 import webob.exc
27 28
28 29 from vcsserver import pygrack, exceptions, settings, git_lfs
29 30
30 31
31 32 log = logging.getLogger(__name__)
32 33
33 34
34 35 # propagated from mercurial documentation
35 36 HG_UI_SECTIONS = [
36 37 'alias', 'auth', 'decode/encode', 'defaults', 'diff', 'email', 'extensions',
37 38 'format', 'merge-patterns', 'merge-tools', 'hooks', 'http_proxy', 'smtp',
38 39 'patch', 'paths', 'profiling', 'server', 'trusted', 'ui', 'web',
39 40 ]
40 41
41 42
42 43 class HgWeb(mercurial.hgweb.hgweb_mod.hgweb):
43 44 """Extension of hgweb that simplifies some functions."""
44 45
45 46 def _get_view(self, repo):
46 47 """Views are not supported."""
47 48 return repo
48 49
49 50 def loadsubweb(self):
50 51 """The result is only used in the templater method which is not used."""
51 52 return None
52 53
53 54 def run(self):
54 55 """Unused function so raise an exception if accidentally called."""
55 56 raise NotImplementedError
56 57
57 58 def templater(self, req):
58 59 """Function used in an unreachable code path.
59 60
60 61 This code is unreachable because we guarantee that the HTTP request,
61 62 corresponds to a Mercurial command. See the is_hg method. So, we are
62 63 never going to get a user-visible url.
63 64 """
64 65 raise NotImplementedError
65 66
66 67 def archivelist(self, nodeid):
67 68 """Unused function so raise an exception if accidentally called."""
68 69 raise NotImplementedError
69 70
70 def run_wsgi(self, req):
71 """Check the request has a valid command, failing fast otherwise."""
71 def __call__(self, environ, start_response):
72 """Run the WSGI application.
73
74 This may be called by multiple threads.
75 """
76 req = mercurial.hgweb.request.wsgirequest(environ, start_response)
77 gen = self.run_wsgi(req)
78
79 first_chunk = None
80
81 try:
82 data = gen.next()
83 def first_chunk(): yield data
84 except StopIteration:
85 pass
86
87 if first_chunk:
88 return itertools.chain(first_chunk(), gen)
89 return gen
90
91 def _runwsgi(self, req, repo):
72 92 cmd = req.form.get('cmd', [''])[0]
73 93 if not mercurial.hgweb.protocol.iscmd(cmd):
74 94 req.respond(
75 95 mercurial.hgweb.common.ErrorResponse(
76 96 mercurial.hgweb.common.HTTP_BAD_REQUEST),
77 97 mercurial.hgweb.protocol.HGTYPE
78 98 )
79 99 return ['']
80 100
81 return super(HgWeb, self).run_wsgi(req)
101 return super(HgWeb, self)._runwsgi(req, repo)
82 102
83 103
84 104 def make_hg_ui_from_config(repo_config):
85 105 baseui = mercurial.ui.ui()
86 106
87 107 # clean the baseui object
88 108 baseui._ocfg = mercurial.config.config()
89 109 baseui._ucfg = mercurial.config.config()
90 110 baseui._tcfg = mercurial.config.config()
91 111
92 112 for section, option, value in repo_config:
93 113 baseui.setconfig(section, option, value)
94 114
95 115 # make our hgweb quiet so it doesn't print output
96 116 baseui.setconfig('ui', 'quiet', 'true')
97 117
98 118 return baseui
99 119
100 120
101 121 def update_hg_ui_from_hgrc(baseui, repo_path):
102 122 path = os.path.join(repo_path, '.hg', 'hgrc')
103 123
104 124 if not os.path.isfile(path):
105 125 log.debug('hgrc file is not present at %s, skipping...', path)
106 126 return
107 127 log.debug('reading hgrc from %s', path)
108 128 cfg = mercurial.config.config()
109 129 cfg.read(path)
110 130 for section in HG_UI_SECTIONS:
111 131 for k, v in cfg.items(section):
112 132 log.debug('settings ui from file: [%s] %s=%s', section, k, v)
113 133 baseui.setconfig(section, k, v)
114 134
115 135
116 136 def create_hg_wsgi_app(repo_path, repo_name, config):
117 137 """
118 138 Prepares a WSGI application to handle Mercurial requests.
119 139
120 140 :param config: is a list of 3-item tuples representing a ConfigObject
121 141 (it is the serialized version of the config object).
122 142 """
123 143 log.debug("Creating Mercurial WSGI application")
124 144
125 145 baseui = make_hg_ui_from_config(config)
126 146 update_hg_ui_from_hgrc(baseui, repo_path)
127 147
128 148 try:
129 149 return HgWeb(repo_path, name=repo_name, baseui=baseui)
130 150 except mercurial.error.RequirementError as exc:
131 151 raise exceptions.RequirementException(exc)
132 152
133 153
134 154 class GitHandler(object):
135 155 """
136 156 Handler for Git operations like push/pull etc
137 157 """
138 158 def __init__(self, repo_location, repo_name, git_path, update_server_info,
139 159 extras):
140 160 if not os.path.isdir(repo_location):
141 161 raise OSError(repo_location)
142 162 self.content_path = repo_location
143 163 self.repo_name = repo_name
144 164 self.repo_location = repo_location
145 165 self.extras = extras
146 166 self.git_path = git_path
147 167 self.update_server_info = update_server_info
148 168
149 169 def __call__(self, environ, start_response):
150 170 app = webob.exc.HTTPNotFound()
151 171 candidate_paths = (
152 172 self.content_path, os.path.join(self.content_path, '.git'))
153 173
154 174 for content_path in candidate_paths:
155 175 try:
156 176 app = pygrack.GitRepository(
157 177 self.repo_name, content_path, self.git_path,
158 178 self.update_server_info, self.extras)
159 179 break
160 180 except OSError:
161 181 continue
162 182
163 183 return app(environ, start_response)
164 184
165 185
166 186 def create_git_wsgi_app(repo_path, repo_name, config):
167 187 """
168 188 Creates a WSGI application to handle Git requests.
169 189
170 190 :param config: is a dictionary holding the extras.
171 191 """
172 192 git_path = settings.GIT_EXECUTABLE
173 193 update_server_info = config.pop('git_update_server_info')
174 194 app = GitHandler(
175 195 repo_path, repo_name, git_path, update_server_info, config)
176 196
177 197 return app
178 198
179 199
180 200 class GitLFSHandler(object):
181 201 """
182 202 Handler for Git LFS operations
183 203 """
184 204
185 205 def __init__(self, repo_location, repo_name, git_path, update_server_info,
186 206 extras):
187 207 if not os.path.isdir(repo_location):
188 208 raise OSError(repo_location)
189 209 self.content_path = repo_location
190 210 self.repo_name = repo_name
191 211 self.repo_location = repo_location
192 212 self.extras = extras
193 213 self.git_path = git_path
194 214 self.update_server_info = update_server_info
195 215
196 216 def get_app(self, git_lfs_enabled, git_lfs_store_path):
197 217 app = git_lfs.create_app(git_lfs_enabled, git_lfs_store_path)
198 218 return app
199 219
200 220
201 221 def create_git_lfs_wsgi_app(repo_path, repo_name, config):
202 222 git_path = settings.GIT_EXECUTABLE
203 223 update_server_info = config.pop('git_update_server_info')
204 224 git_lfs_enabled = config.pop('git_lfs_enabled')
205 225 git_lfs_store_path = config.pop('git_lfs_store_path')
206 226 app = GitLFSHandler(
207 227 repo_path, repo_name, git_path, update_server_info, config)
208 228
209 229 return app.get_app(git_lfs_enabled, git_lfs_store_path)
General Comments 0
You need to be logged in to leave comments. Login now