##// END OF EJS Templates
fix pygrack call on non-bare repos
marcink -
r2502:9374eeca beta
parent child Browse files
Show More
@@ -1,199 +1,200
1 1 import os
2 2 import socket
3 3 import logging
4 4 import subprocess
5 5
6 6 from webob import Request, Response, exc
7 7
8 8 from rhodecode.lib import subprocessio
9 9
10 10 log = logging.getLogger(__name__)
11 11
12 12
13 13 class FileWrapper(object):
14 14
15 15 def __init__(self, fd, content_length):
16 16 self.fd = fd
17 17 self.content_length = content_length
18 18 self.remain = content_length
19 19
20 20 def read(self, size):
21 21 if size <= self.remain:
22 22 try:
23 23 data = self.fd.read(size)
24 24 except socket.error:
25 25 raise IOError(self)
26 26 self.remain -= size
27 27 elif self.remain:
28 28 data = self.fd.read(self.remain)
29 29 self.remain = 0
30 30 else:
31 31 data = None
32 32 return data
33 33
34 34 def __repr__(self):
35 35 return '<FileWrapper %s len: %s, read: %s>' % (
36 36 self.fd, self.content_length, self.content_length - self.remain
37 37 )
38 38
39 39
40 40 class GitRepository(object):
41 41 git_folder_signature = set(['config', 'head', 'info', 'objects', 'refs'])
42 42 commands = ['git-upload-pack', 'git-receive-pack']
43 43
44 44 def __init__(self, repo_name, content_path, username):
45 45 files = set([f.lower() for f in os.listdir(content_path)])
46 46 if not (self.git_folder_signature.intersection(files)
47 47 == self.git_folder_signature):
48 48 raise OSError('%s missing git signature' % content_path)
49 49 self.content_path = content_path
50 50 self.valid_accepts = ['application/x-%s-result' %
51 51 c for c in self.commands]
52 52 self.repo_name = repo_name
53 53 self.username = username
54 54
55 55 def _get_fixedpath(self, path):
56 56 """
57 57 Small fix for repo_path
58 58
59 59 :param path:
60 60 :type path:
61 61 """
62 62 return path.split(self.repo_name, 1)[-1].strip('/')
63 63
64 64 def inforefs(self, request, environ):
65 65 """
66 66 WSGI Response producer for HTTP GET Git Smart
67 67 HTTP /info/refs request.
68 68 """
69 69
70 70 git_command = request.GET['service']
71 71 if git_command not in self.commands:
72 72 log.debug('command %s not allowed' % git_command)
73 73 return exc.HTTPMethodNotAllowed()
74 74
75 75 # note to self:
76 76 # please, resist the urge to add '\n' to git capture and increment
77 77 # line count by 1.
78 78 # The code in Git client not only does NOT need '\n', but actually
79 79 # blows up if you sprinkle "flush" (0000) as "0001\n".
80 80 # It reads binary, per number of bytes specified.
81 81 # if you do add '\n' as part of data, count it.
82 82 smart_server_advert = '# service=%s' % git_command
83 83 try:
84 84 out = subprocessio.SubprocessIOChunker(
85 85 r'git %s --stateless-rpc --advertise-refs "%s"' % (
86 86 git_command[4:], self.content_path),
87 87 starting_values=[
88 88 str(hex(len(smart_server_advert) + 4)[2:]
89 89 .rjust(4, '0') + smart_server_advert + '0000')
90 90 ]
91 91 )
92 92 except EnvironmentError, e:
93 93 log.exception(e)
94 94 raise exc.HTTPExpectationFailed()
95 95 resp = Response()
96 96 resp.content_type = 'application/x-%s-advertisement' % str(git_command)
97 97 resp.app_iter = out
98 98 return resp
99 99
100 100 def backend(self, request, environ):
101 101 """
102 102 WSGI Response producer for HTTP POST Git Smart HTTP requests.
103 103 Reads commands and data from HTTP POST's body.
104 104 returns an iterator obj with contents of git command's
105 105 response to stdout
106 106 """
107 107 git_command = self._get_fixedpath(request.path_info)
108 108 if git_command not in self.commands:
109 109 log.debug('command %s not allowed' % git_command)
110 110 return exc.HTTPMethodNotAllowed()
111 111
112 112 if 'CONTENT_LENGTH' in environ:
113 113 inputstream = FileWrapper(environ['wsgi.input'],
114 114 request.content_length)
115 115 else:
116 116 inputstream = environ['wsgi.input']
117 117
118 118 try:
119 119 gitenv = os.environ
120 120 from rhodecode import CONFIG
121 121 from rhodecode.lib.base import _get_ip_addr
122 122 gitenv['RHODECODE_USER'] = self.username
123 123 gitenv['RHODECODE_CONFIG_IP'] = _get_ip_addr(environ)
124 124 # forget all configs
125 125 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
126 126 # we need current .ini file used to later initialize rhodecode
127 127 # env and connect to db
128 128 gitenv['RHODECODE_CONFIG_FILE'] = CONFIG['__file__']
129 129 opts = dict(
130 130 env=gitenv,
131 131 cwd=os.getcwd()
132 132 )
133 133 out = subprocessio.SubprocessIOChunker(
134 134 r'git %s --stateless-rpc "%s"' % (git_command[4:],
135 135 self.content_path),
136 136 inputstream=inputstream,
137 137 **opts
138 138 )
139 139 except EnvironmentError, e:
140 140 log.exception(e)
141 141 raise exc.HTTPExpectationFailed()
142 142
143 143 if git_command in [u'git-receive-pack']:
144 144 # updating refs manually after each push.
145 145 # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
146 146 subprocess.call(u'git --git-dir "%s" '
147 147 'update-server-info' % self.content_path,
148 148 shell=True)
149 149
150 150 resp = Response()
151 151 resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
152 152 resp.app_iter = out
153 153 return resp
154 154
155 155 def __call__(self, environ, start_response):
156 156 request = Request(environ)
157 157 _path = self._get_fixedpath(request.path_info)
158 158 if _path.startswith('info/refs'):
159 159 app = self.inforefs
160 160 elif [a for a in self.valid_accepts if a in request.accept]:
161 161 app = self.backend
162 162 try:
163 163 resp = app(request, environ)
164 164 except exc.HTTPException, e:
165 165 resp = e
166 166 log.exception(e)
167 167 except Exception, e:
168 168 log.exception(e)
169 169 resp = exc.HTTPInternalServerError()
170 170 return resp(environ, start_response)
171 171
172 172
173 173 class GitDirectory(object):
174 174
175 175 def __init__(self, repo_root, repo_name, username):
176 176 repo_location = os.path.join(repo_root, repo_name)
177 177 if not os.path.isdir(repo_location):
178 178 raise OSError(repo_location)
179 179
180 180 self.content_path = repo_location
181 181 self.repo_name = repo_name
182 182 self.repo_location = repo_location
183 183 self.username = username
184 184
185 185 def __call__(self, environ, start_response):
186 186 content_path = self.content_path
187 187 try:
188 188 app = GitRepository(self.repo_name, content_path, self.username)
189 189 except (AssertionError, OSError):
190 190 if os.path.isdir(os.path.join(content_path, '.git')):
191 191 app = GitRepository(self.repo_name,
192 os.path.join(content_path, '.git'))
192 os.path.join(content_path, '.git'),
193 self.username)
193 194 else:
194 195 return exc.HTTPNotFound()(environ, start_response, self.username)
195 196 return app(environ, start_response)
196 197
197 198
198 199 def make_wsgi_app(repo_name, repo_root, username):
199 200 return GitDirectory(repo_root, repo_name, username)
General Comments 0
You need to be logged in to leave comments. Login now