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