##// END OF EJS Templates
fix http NotFound call in pygrack
marcink -
r2594:3a54e1e1 beta
parent child Browse files
Show More
@@ -1,202 +1,202 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, 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 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.base import _get_ip_addr
123 123 gitenv['RHODECODE_USER'] = self.username
124 124 gitenv['RHODECODE_CONFIG_IP'] = _get_ip_addr(environ)
125 125 # forget all configs
126 126 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
127 127 # we need current .ini file used to later initialize rhodecode
128 128 # env and connect to db
129 129 gitenv['RHODECODE_CONFIG_FILE'] = CONFIG['__file__']
130 130 opts = dict(
131 131 env=gitenv,
132 132 cwd=os.getcwd()
133 133 )
134 134 out = subprocessio.SubprocessIOChunker(
135 135 r'git %s --stateless-rpc "%s"' % (git_command[4:],
136 136 self.content_path),
137 137 inputstream=inputstream,
138 138 **opts
139 139 )
140 140 except EnvironmentError, e:
141 141 log.exception(e)
142 142 raise exc.HTTPExpectationFailed()
143 143
144 144 if git_command in [u'git-receive-pack']:
145 145 # updating refs manually after each push.
146 146 # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
147 147 subprocess.call(u'git --git-dir "%s" '
148 148 'update-server-info' % self.content_path,
149 149 shell=True)
150 150
151 151 resp = Response()
152 152 resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
153 153 resp.charset = None
154 154 resp.app_iter = out
155 155 return resp
156 156
157 157 def __call__(self, environ, start_response):
158 158 request = Request(environ)
159 159 _path = self._get_fixedpath(request.path_info)
160 160 if _path.startswith('info/refs'):
161 161 app = self.inforefs
162 162 elif [a for a in self.valid_accepts if a in request.accept]:
163 163 app = self.backend
164 164 try:
165 165 resp = app(request, environ)
166 166 except exc.HTTPException, e:
167 167 resp = e
168 168 log.exception(e)
169 169 except Exception, e:
170 170 log.exception(e)
171 171 resp = exc.HTTPInternalServerError()
172 172 return resp(environ, start_response)
173 173
174 174
175 175 class GitDirectory(object):
176 176
177 177 def __init__(self, repo_root, repo_name, username):
178 178 repo_location = os.path.join(repo_root, repo_name)
179 179 if not os.path.isdir(repo_location):
180 180 raise OSError(repo_location)
181 181
182 182 self.content_path = repo_location
183 183 self.repo_name = repo_name
184 184 self.repo_location = repo_location
185 185 self.username = username
186 186
187 187 def __call__(self, environ, start_response):
188 188 content_path = self.content_path
189 189 try:
190 190 app = GitRepository(self.repo_name, content_path, self.username)
191 191 except (AssertionError, OSError):
192 192 if os.path.isdir(os.path.join(content_path, '.git')):
193 193 app = GitRepository(self.repo_name,
194 194 os.path.join(content_path, '.git'),
195 195 self.username)
196 196 else:
197 return exc.HTTPNotFound()(environ, start_response, self.username)
197 return exc.HTTPNotFound()(environ, start_response)
198 198 return app(environ, start_response)
199 199
200 200
201 201 def make_wsgi_app(repo_name, repo_root, username):
202 202 return GitDirectory(repo_root, repo_name, username)
General Comments 0
You need to be logged in to leave comments. Login now