comparison rhodecode/lib/middleware/simplegit.py @ 1496:f4fed0b32103 beta

Rewrote git middleware with the same pattern as recent fix for #176 - additionally fixed dulwich server bug, that used old deprecated method. This fix caused huge improvement of speed for fetching git repos - moved simplehg and simplegit middleware into begining of pylons callstack. This low level middleware don't need to run any of the middlewares except each other.
author Marcin Kuzminski <marcin@python-works.com>
date Thu, 29 Sep 2011 23:34:47 +0300
parents b7563ad4e7ee
children 71738535ed78
comparison
equal deleted inserted replaced
1495:5bd42279930c 1496:f4fed0b32103
42 objects_iter = self.repo.fetch_objects( 42 objects_iter = self.repo.fetch_objects(
43 graph_walker.determine_wants, graph_walker, self.progress, 43 graph_walker.determine_wants, graph_walker, self.progress,
44 get_tagged=self.get_tagged) 44 get_tagged=self.get_tagged)
45 45
46 # Do they want any objects? 46 # Do they want any objects?
47 if len(objects_iter) == 0: 47 if objects_iter is None or len(objects_iter) == 0:
48 return 48 return
49 49
50 self.progress("counting objects: %d, done.\n" % len(objects_iter)) 50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 dulserver.write_pack_data(dulserver.ProtocolFile(None, write), 51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
52 objects_iter, len(objects_iter)) 52 objects_iter, len(objects_iter))
53 messages = [] 53 messages = []
54 messages.append('thank you for using rhodecode') 54 messages.append('thank you for using rhodecode')
55 55
56 for msg in messages: 56 for msg in messages:
94 class SimpleGit(object): 94 class SimpleGit(object):
95 95
96 def __init__(self, application, config): 96 def __init__(self, application, config):
97 self.application = application 97 self.application = application
98 self.config = config 98 self.config = config
99 #authenticate this git request using 99 # base path of repo locations
100 self.basepath = self.config['base_path']
101 #authenticate this mercurial request using authfunc
100 self.authenticate = AuthBasicAuthenticator('', authfunc) 102 self.authenticate = AuthBasicAuthenticator('', authfunc)
101 self.ipaddr = '0.0.0.0'
102 self.repo_name = None
103 self.username = None
104 self.action = None
105 103
106 def __call__(self, environ, start_response): 104 def __call__(self, environ, start_response):
107 if not is_git(environ): 105 if not is_git(environ):
108 return self.application(environ, start_response) 106 return self.application(environ, start_response)
109 107
110 proxy_key = 'HTTP_X_REAL_IP' 108 proxy_key = 'HTTP_X_REAL_IP'
111 def_key = 'REMOTE_ADDR' 109 def_key = 'REMOTE_ADDR'
112 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0')) 110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
111 username = None
113 # skip passing error to error controller 112 # skip passing error to error controller
114 environ['pylons.status_code_redirect'] = True 113 environ['pylons.status_code_redirect'] = True
115 114
116 #====================================================================== 115 #======================================================================
117 # GET ACTION PULL or PUSH 116 # EXTRACT REPOSITORY NAME FROM ENV
118 #====================================================================== 117 #======================================================================
119 self.action = self.__get_action(environ)
120 try: 118 try:
121 #================================================================== 119 repo_name = self.__get_repository(environ)
122 # GET REPOSITORY NAME 120 log.debug('Extracted repo name is %s' % repo_name)
123 #==================================================================
124 self.repo_name = self.__get_repository(environ)
125 except: 121 except:
126 return HTTPInternalServerError()(environ, start_response) 122 return HTTPInternalServerError()(environ, start_response)
127 123
128 #====================================================================== 124 #======================================================================
125 # GET ACTION PULL or PUSH
126 #======================================================================
127 action = self.__get_action(environ)
128
129 #======================================================================
129 # CHECK ANONYMOUS PERMISSION 130 # CHECK ANONYMOUS PERMISSION
130 #====================================================================== 131 #======================================================================
131 if self.action in ['pull', 'push']: 132 if action in ['pull', 'push']:
132 anonymous_user = self.__get_user('default') 133 anonymous_user = self.__get_user('default')
133 self.username = anonymous_user.username 134 username = anonymous_user.username
134 anonymous_perm = self.__check_permission(self.action, 135 anonymous_perm = self.__check_permission(action,
135 anonymous_user, 136 anonymous_user,
136 self.repo_name) 137 repo_name)
137 138
138 if anonymous_perm is not True or anonymous_user.active is False: 139 if anonymous_perm is not True or anonymous_user.active is False:
139 if anonymous_perm is not True: 140 if anonymous_perm is not True:
140 log.debug('Not enough credentials to access this ' 141 log.debug('Not enough credentials to access this '
141 'repository as anonymous user') 142 'repository as anonymous user')
160 #============================================================== 161 #==============================================================
161 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM 162 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
162 # BASIC AUTH 163 # BASIC AUTH
163 #============================================================== 164 #==============================================================
164 165
165 if self.action in ['pull', 'push']: 166 if action in ['pull', 'push']:
166 username = REMOTE_USER(environ) 167 username = REMOTE_USER(environ)
167 try: 168 try:
168 user = self.__get_user(username) 169 user = self.__get_user(username)
169 self.username = user.username 170 username = user.username
170 except: 171 except:
171 log.error(traceback.format_exc()) 172 log.error(traceback.format_exc())
172 return HTTPInternalServerError()(environ, 173 return HTTPInternalServerError()(environ,
173 start_response) 174 start_response)
174 175
175 #check permissions for this repository 176 #check permissions for this repository
176 perm = self.__check_permission(self.action, user, 177 perm = self.__check_permission(action, user,
177 self.repo_name) 178 repo_name)
178 if perm is not True: 179 if perm is not True:
179 return HTTPForbidden()(environ, start_response) 180 return HTTPForbidden()(environ, start_response)
180 181
181 self.extras = {'ip': self.ipaddr, 182 extras = {'ip': ipaddr,
182 'username': self.username, 183 'username': username,
183 'action': self.action, 184 'action': action,
184 'repository': self.repo_name} 185 'repository': repo_name}
185 186
186 #=================================================================== 187 #===================================================================
187 # GIT REQUEST HANDLING 188 # GIT REQUEST HANDLING
188 #=================================================================== 189 #===================================================================
189 self.basepath = self.config['base_path'] 190
190 self.repo_path = os.path.join(self.basepath, self.repo_name) 191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
191 #quick check if that dir exists... 192 log.debug('Repository path is %s' % repo_path)
192 if check_repo_fast(self.repo_name, self.basepath): 193
194 # quick check if that dir exists...
195 if check_repo_fast(repo_name, self.basepath):
193 return HTTPNotFound()(environ, start_response) 196 return HTTPNotFound()(environ, start_response)
197
194 try: 198 try:
195 app = self.__make_app() 199 #invalidate cache on push
196 except: 200 if action == 'push':
201 self.__invalidate_cache(repo_name)
202
203 app = self.__make_app(repo_name, repo_path)
204 return app(environ, start_response)
205 except Exception:
197 log.error(traceback.format_exc()) 206 log.error(traceback.format_exc())
198 return HTTPInternalServerError()(environ, start_response) 207 return HTTPInternalServerError()(environ, start_response)
199 208
200 #invalidate cache on push 209 def __make_app(self, repo_name, repo_path):
201 if self.action == 'push': 210 """
202 self.__invalidate_cache(self.repo_name) 211 Make an wsgi application using dulserver
203 212
204 return app(environ, start_response) 213 :param repo_name: name of the repository
205 214 :param repo_path: full path to the repository
206 def __make_app(self): 215 """
207 _d = {'/' + self.repo_name: Repo(self.repo_path)} 216
217 _d = {'/' + repo_name: Repo(repo_path)}
208 backend = dulserver.DictBackend(_d) 218 backend = dulserver.DictBackend(_d)
209 gitserve = HTTPGitApplication(backend) 219 gitserve = HTTPGitApplication(backend)
210 220
211 return gitserve 221 return gitserve
212 222
213 def __check_permission(self, action, user, repo_name): 223 def __check_permission(self, action, user, repo_name):
214 """Checks permissions using action (push/pull) user and repository 224 """
225 Checks permissions using action (push/pull) user and repository
215 name 226 name
216 227
217 :param action: push or pull action 228 :param action: push or pull action
218 :param user: user instance 229 :param user: user instance
219 :param repo_name: repository name 230 :param repo_name: repository name
233 return False 244 return False
234 245
235 return True 246 return True
236 247
237 def __get_repository(self, environ): 248 def __get_repository(self, environ):
238 """Get's repository name out of PATH_INFO header 249 """
250 Get's repository name out of PATH_INFO header
239 251
240 :param environ: environ where PATH_INFO is stored 252 :param environ: environ where PATH_INFO is stored
241 """ 253 """
242 try: 254 try:
243 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) 255 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
272 def __invalidate_cache(self, repo_name): 284 def __invalidate_cache(self, repo_name):
273 """we know that some change was made to repositories and we should 285 """we know that some change was made to repositories and we should
274 invalidate the cache to see the changes right away but only for 286 invalidate the cache to see the changes right away but only for
275 push requests""" 287 push requests"""
276 invalidate_cache('get_repo_cached_%s' % repo_name) 288 invalidate_cache('get_repo_cached_%s' % repo_name)
289