##// END OF EJS Templates
reset charset for git rpc cals also
marcink -
r2581:ee980ead beta
parent child Browse files
Show More
@@ -1,201 +1,202
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 resp.charset = None
153 154 resp.app_iter = out
154 155 return resp
155 156
156 157 def __call__(self, environ, start_response):
157 158 request = Request(environ)
158 159 _path = self._get_fixedpath(request.path_info)
159 160 if _path.startswith('info/refs'):
160 161 app = self.inforefs
161 162 elif [a for a in self.valid_accepts if a in request.accept]:
162 163 app = self.backend
163 164 try:
164 165 resp = app(request, environ)
165 166 except exc.HTTPException, e:
166 167 resp = e
167 168 log.exception(e)
168 169 except Exception, e:
169 170 log.exception(e)
170 171 resp = exc.HTTPInternalServerError()
171 172 return resp(environ, start_response)
172 173
173 174
174 175 class GitDirectory(object):
175 176
176 177 def __init__(self, repo_root, repo_name, username):
177 178 repo_location = os.path.join(repo_root, repo_name)
178 179 if not os.path.isdir(repo_location):
179 180 raise OSError(repo_location)
180 181
181 182 self.content_path = repo_location
182 183 self.repo_name = repo_name
183 184 self.repo_location = repo_location
184 185 self.username = username
185 186
186 187 def __call__(self, environ, start_response):
187 188 content_path = self.content_path
188 189 try:
189 190 app = GitRepository(self.repo_name, content_path, self.username)
190 191 except (AssertionError, OSError):
191 192 if os.path.isdir(os.path.join(content_path, '.git')):
192 193 app = GitRepository(self.repo_name,
193 194 os.path.join(content_path, '.git'),
194 195 self.username)
195 196 else:
196 197 return exc.HTTPNotFound()(environ, start_response, self.username)
197 198 return app(environ, start_response)
198 199
199 200
200 201 def make_wsgi_app(repo_name, repo_root, username):
201 202 return GitDirectory(repo_root, repo_name, username)
General Comments 0
You need to be logged in to leave comments. Login now