##// END OF EJS Templates
fixed #545, exception during cloning of non-bare repositories
marcink -
r2764:c7a27b04 beta
parent child Browse files
Show More
@@ -1,201 +1,200 b''
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, extras):
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.extras = extras
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.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 server_advert = '# service=%s' % git_command
83 83 packet_len = str(hex(len(server_advert) + 4)[2:].rjust(4, '0')).lower()
84 84 try:
85 85 out = subprocessio.SubprocessIOChunker(
86 86 r'git %s --stateless-rpc --advertise-refs "%s"' % (
87 87 git_command[4:], self.content_path),
88 88 starting_values=[
89 89 packet_len + 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.charset = None
98 98 resp.app_iter = out
99 99 return resp
100 100
101 101 def backend(self, request, environ):
102 102 """
103 103 WSGI Response producer for HTTP POST Git Smart HTTP requests.
104 104 Reads commands and data from HTTP POST's body.
105 105 returns an iterator obj with contents of git command's
106 106 response to stdout
107 107 """
108 108 git_command = self._get_fixedpath(request.path_info)
109 109 if git_command not in self.commands:
110 110 log.debug('command %s not allowed' % git_command)
111 111 return exc.HTTPMethodNotAllowed()
112 112
113 113 if 'CONTENT_LENGTH' in environ:
114 114 inputstream = FileWrapper(environ['wsgi.input'],
115 115 request.content_length)
116 116 else:
117 117 inputstream = environ['wsgi.input']
118 118
119 119 try:
120 120 gitenv = os.environ
121 121 from rhodecode import CONFIG
122 122 from rhodecode.lib.compat import json
123 123 gitenv['RHODECODE_EXTRAS'] = json.dumps(self.extras)
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.charset = None
153 153 resp.app_iter = out
154 154 return resp
155 155
156 156 def __call__(self, environ, start_response):
157 157 request = Request(environ)
158 158 _path = self._get_fixedpath(request.path_info)
159 159 if _path.startswith('info/refs'):
160 160 app = self.inforefs
161 161 elif [a for a in self.valid_accepts if a in request.accept]:
162 162 app = self.backend
163 163 try:
164 164 resp = app(request, environ)
165 165 except exc.HTTPException, e:
166 166 resp = e
167 167 log.exception(e)
168 168 except Exception, e:
169 169 log.exception(e)
170 170 resp = exc.HTTPInternalServerError()
171 171 return resp(environ, start_response)
172 172
173 173
174 174 class GitDirectory(object):
175 175
176 176 def __init__(self, repo_root, repo_name, extras):
177 177 repo_location = os.path.join(repo_root, repo_name)
178 178 if not os.path.isdir(repo_location):
179 179 raise OSError(repo_location)
180 180
181 181 self.content_path = repo_location
182 182 self.repo_name = repo_name
183 183 self.repo_location = repo_location
184 184 self.extras = extras
185 185
186 186 def __call__(self, environ, start_response):
187 187 content_path = self.content_path
188 188 try:
189 189 app = GitRepository(self.repo_name, content_path, self.extras)
190 190 except (AssertionError, OSError):
191 if os.path.isdir(os.path.join(content_path, '.git')):
192 app = GitRepository(self.repo_name,
193 os.path.join(content_path, '.git'),
194 self.username)
191 content_path = os.path.join(content_path, '.git')
192 if os.path.isdir(content_path):
193 app = GitRepository(self.repo_name, content_path, self.extras)
195 194 else:
196 195 return exc.HTTPNotFound()(environ, start_response)
197 196 return app(environ, start_response)
198 197
199 198
200 199 def make_wsgi_app(repo_name, repo_root, extras):
201 200 return GitDirectory(repo_root, repo_name, extras)
General Comments 0
You need to be logged in to leave comments. Login now