##// END OF EJS Templates
implemented cache for repeated queries in simplehg mercurial requests
marcink -
r343:64849630 default
parent child Browse files
Show More
@@ -44,11 +44,13 b' cache_dir = %(here)s/data'
44 ####################################
44 ####################################
45 beaker.cache.data_dir=/%(here)s/data/cache/data
45 beaker.cache.data_dir=/%(here)s/data/cache/data
46 beaker.cache.lock_dir=/%(here)s/data/cache/lock
46 beaker.cache.lock_dir=/%(here)s/data/cache/lock
47 beaker.cache.regions=short_term,long_term
47 beaker.cache.regions=super_short_term,short_term,long_term
48 beaker.cache.long_term.type=memory
48 beaker.cache.long_term.type=memory
49 beaker.cache.long_term.expire=36000
49 beaker.cache.long_term.expire=36000
50 beaker.cache.short_term.type=memory
50 beaker.cache.short_term.type=memory
51 beaker.cache.short_term.expire=60
51 beaker.cache.short_term.expire=60
52 beaker.cache.super_short_term.type=memory
53 beaker.cache.super_short_term.expire=10
52
54
53 ################################################################################
55 ################################################################################
54 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
56 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
@@ -39,17 +39,18 b' static_files = false'
39 lang=en
39 lang=en
40 cache_dir = %(here)s/data
40 cache_dir = %(here)s/data
41
41
42
43 ####################################
42 ####################################
44 ### BEAKER CACHE ####
43 ### BEAKER CACHE ####
45 ####################################
44 ####################################
46 beaker.cache.data_dir=/%(here)s/data/cache/data
45 beaker.cache.data_dir=/%(here)s/data/cache/data
47 beaker.cache.lock_dir=/%(here)s/data/cache/lock
46 beaker.cache.lock_dir=/%(here)s/data/cache/lock
48 beaker.cache.regions=short_term,long_term
47 beaker.cache.regions=super_short_term,short_term,long_term
49 beaker.cache.long_term.type=memory
48 beaker.cache.long_term.type=memory
50 beaker.cache.long_term.expire=36000
49 beaker.cache.long_term.expire=36000
51 beaker.cache.short_term.type=memory
50 beaker.cache.short_term.type=memory
52 beaker.cache.short_term.expire=60
51 beaker.cache.short_term.expire=60
52 beaker.cache.super_short_term.type=memory
53 beaker.cache.super_short_term.expire=10
53
54
54 ################################################################################
55 ################################################################################
55 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
56 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
@@ -22,18 +22,18 b' Created on April 4, 2010'
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25 from beaker.cache import cache_region
26 from functools import wraps
26 from functools import wraps
27 from pylons import session, url, request
27 from pylons import config, session, url, request
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons_app.lib.utils import get_repo_slug
29 from pylons_app.model import meta
30 from pylons_app.model import meta
30 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
31 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
31 from pylons_app.lib.utils import get_repo_slug
32 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.exc import OperationalError
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 import crypt
34 import crypt
35 import logging
35 import logging
36 from pylons import config
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 def get_crypt_password(password):
39 def get_crypt_password(password):
@@ -43,11 +43,17 b' def get_crypt_password(password):'
43 """
43 """
44 return crypt.crypt(password, '6a')
44 return crypt.crypt(password, '6a')
45
45
46
47 @cache_region('super_short_term', 'cached_user')
48 def get_user_cached(username):
49 sa = meta.Session
50 user = sa.query(User).filter(User.username == username).one()
51 return user
52
46 def authfunc(environ, username, password):
53 def authfunc(environ, username, password):
47 sa = meta.Session
48 password_crypt = get_crypt_password(password)
54 password_crypt = get_crypt_password(password)
49 try:
55 try:
50 user = sa.query(User).filter(User.username == username).one()
56 user = get_user_cached(username)
51 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
57 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
52 log.error(e)
58 log.error(e)
53 user = None
59 user = None
@@ -2,7 +2,7 b''
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle mercurial api calls
3 # middleware to handle mercurial api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
@@ -27,21 +27,23 b" It's implemented with basic auth functio"
27 """
27 """
28 from datetime import datetime
28 from datetime import datetime
29 from itertools import chain
29 from itertools import chain
30 from mercurial.error import RepoError
30 from mercurial.hgweb import hgweb
31 from mercurial.hgweb import hgweb
31 from mercurial.hgweb.request import wsgiapplication
32 from mercurial.hgweb.request import wsgiapplication
32 from mercurial.error import RepoError
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
34 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from pylons_app.lib.auth import authfunc, HasPermissionAnyMiddleware
35 from pylons_app.lib.auth import authfunc, HasPermissionAnyMiddleware, \
36 get_user_cached
36 from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache, \
37 from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache, \
37 check_repo_fast
38 check_repo_fast
38 from pylons_app.model import meta
39 from pylons_app.model import meta
39 from pylons_app.model.db import UserLog, User
40 from pylons_app.model.db import UserLog, User
40 import pylons_app.lib.helpers as h
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
42 import logging
42 import logging
43 import os
43 import os
44 import pylons_app.lib.helpers as h
44 import traceback
45 import traceback
46
45 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
46
48
47 class SimpleHg(object):
49 class SimpleHg(object):
@@ -56,86 +58,84 b' class SimpleHg(object):'
56 def __call__(self, environ, start_response):
58 def __call__(self, environ, start_response):
57 if not is_mercurial(environ):
59 if not is_mercurial(environ):
58 return self.application(environ, start_response)
60 return self.application(environ, start_response)
59 else:
61
60 #===================================================================
62 #===================================================================
61 # AUTHENTICATE THIS MERCURIAL REQUEST
63 # AUTHENTICATE THIS MERCURIAL REQUEST
62 #===================================================================
64 #===================================================================
63 username = REMOTE_USER(environ)
65 username = REMOTE_USER(environ)
64 if not username:
66 if not username:
65 result = self.authenticate(environ)
67 result = self.authenticate(environ)
66 if isinstance(result, str):
68 if isinstance(result, str):
67 AUTH_TYPE.update(environ, 'basic')
69 AUTH_TYPE.update(environ, 'basic')
68 REMOTE_USER.update(environ, result)
70 REMOTE_USER.update(environ, result)
69 else:
71 else:
70 return result.wsgi_application(environ, start_response)
72 return result.wsgi_application(environ, start_response)
71
73
74 try:
75 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
76 except:
77 log.error(traceback.format_exc())
78 return HTTPInternalServerError()(environ, start_response)
79
80 #===================================================================
81 # CHECK PERMISSIONS FOR THIS REQUEST
82 #===================================================================
83 action = self.__get_action(environ)
84 if action:
85 username = self.__get_environ_user(environ)
72 try:
86 try:
73 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
87 user = self.__get_user(username)
74 except:
88 except:
75 log.error(traceback.format_exc())
89 log.error(traceback.format_exc())
76 return HTTPInternalServerError()(environ, start_response)
90 return HTTPInternalServerError()(environ, start_response)
91 #check permissions for this repository
92 if action == 'pull':
93 if not HasPermissionAnyMiddleware('repository.read',
94 'repository.write',
95 'repository.admin')\
96 (user, repo_name):
97 return HTTPForbidden()(environ, start_response)
98 if action == 'push':
99 if not HasPermissionAnyMiddleware('repository.write',
100 'repository.admin')\
101 (user, repo_name):
102 return HTTPForbidden()(environ, start_response)
77
103
78 #===================================================================
104 #log action
79 # CHECK PERMISSIONS FOR THIS REQUEST
105 proxy_key = 'HTTP_X_REAL_IP'
80 #===================================================================
106 def_key = 'REMOTE_ADDR'
81 action = self.__get_action(environ)
107 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
82 if action:
108 self.__log_user_action(user, action, repo_name, ipaddr)
83 username = self.__get_environ_user(environ)
109
84 try:
110 #===================================================================
85 sa = meta.Session
111 # MERCURIAL REQUEST HANDLING
86 user = sa.query(User)\
112 #===================================================================
87 .filter(User.username == username).one()
113 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
88 except:
114 self.baseui = make_ui('db')
89 log.error(traceback.format_exc())
115 self.basepath = self.config['base_path']
90 return HTTPInternalServerError()(environ, start_response)
116 self.repo_path = os.path.join(self.basepath, repo_name)
91 #check permissions for this repository
92 if action == 'pull':
93 if not HasPermissionAnyMiddleware('repository.read',
94 'repository.write',
95 'repository.admin')\
96 (user, repo_name):
97 return HTTPForbidden()(environ, start_response)
98 if action == 'push':
99 if not HasPermissionAnyMiddleware('repository.write',
100 'repository.admin')\
101 (user, repo_name):
102 return HTTPForbidden()(environ, start_response)
103
104 #log action
105 proxy_key = 'HTTP_X_REAL_IP'
106 def_key = 'REMOTE_ADDR'
107 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
108 self.__log_user_action(user, action, repo_name, ipaddr)
109
110 #===================================================================
111 # MERCURIAL REQUEST HANDLING
112 #===================================================================
113 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
114 self.baseui = make_ui('db')
115 self.basepath = self.config['base_path']
116 self.repo_path = os.path.join(self.basepath, repo_name)
117
117
118 #quick check if that dir exists...
118 #quick check if that dir exists...
119 if check_repo_fast(repo_name, self.basepath):
119 if check_repo_fast(repo_name, self.basepath):
120 return HTTPNotFound()(environ, start_response)
121 try:
122 app = wsgiapplication(self.__make_app)
123 except RepoError as e:
124 if str(e).find('not found') != -1:
120 return HTTPNotFound()(environ, start_response)
125 return HTTPNotFound()(environ, start_response)
121 try:
126 except Exception:
122 app = wsgiapplication(self.__make_app)
127 log.error(traceback.format_exc())
123 except RepoError as e:
128 return HTTPInternalServerError()(environ, start_response)
124 if str(e).find('not found') != -1:
129
125 return HTTPNotFound()(environ, start_response)
130 #invalidate cache on push
126 except Exception:
131 if action == 'push':
127 log.error(traceback.format_exc())
132 self.__invalidate_cache(repo_name)
128 return HTTPInternalServerError()(environ, start_response)
133 messages = []
129
134 messages.append('thank you for using hg-app')
130 #invalidate cache on push
135
131 if action == 'push':
136 return self.msg_wrapper(app, environ, start_response, messages)
132 self.__invalidate_cache(repo_name)
137 else:
133 messages = []
138 return app(environ, start_response)
134 messages.append('thank you for using hg-app')
135
136 return self.msg_wrapper(app, environ, start_response, messages)
137 else:
138 return app(environ, start_response)
139
139
140
140
141 def msg_wrapper(self, app, environ, start_response, messages=[]):
141 def msg_wrapper(self, app, environ, start_response, messages=[]):
@@ -160,6 +160,11 b' class SimpleHg(object):'
160 def __get_environ_user(self, environ):
160 def __get_environ_user(self, environ):
161 return environ.get('REMOTE_USER')
161 return environ.get('REMOTE_USER')
162
162
163 def __get_user(self, username):
164 return get_user_cached(username)
165
166
167
163 def __get_size(self, repo_path, content_size):
168 def __get_size(self, repo_path, content_size):
164 size = int(content_size)
169 size = int(content_size)
165 for path, dirs, files in os.walk(repo_path):
170 for path, dirs, files in os.walk(repo_path):
@@ -16,6 +16,7 b''
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 from beaker.cache import cache_region
19
20
20 """
21 """
21 Created on April 18, 2010
22 Created on April 18, 2010
@@ -75,6 +76,13 b' def check_repo(repo_name, base_path, ver'
75 log.info('%s repo is free for creation', repo_name)
76 log.info('%s repo is free for creation', repo_name)
76 return True
77 return True
77
78
79
80 @cache_region('super_short_term', 'cached_hg_ui')
81 def get_hg_ui_cached():
82 from pylons_app.model.meta import Session
83 sa = Session()
84 return sa.query(HgAppUi).all()
85
78 def make_ui(read_from='file', path=None, checkpaths=True):
86 def make_ui(read_from='file', path=None, checkpaths=True):
79 """
87 """
80 A function that will read python rc files or database
88 A function that will read python rc files or database
@@ -112,10 +120,7 b" def make_ui(read_from='file', path=None,"
112
120
113
121
114 elif read_from == 'db':
122 elif read_from == 'db':
115 from pylons_app.model.meta import Session
123 hg_ui = get_hg_ui_cached()
116 sa = Session()
117
118 hg_ui = sa.query(HgAppUi).all()
119 for ui_ in hg_ui:
124 for ui_ in hg_ui:
120 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
125 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
121
126
General Comments 0
You need to be logged in to leave comments. Login now