Show More
@@ -138,6 +138,119 b' class _multifile(object):' | |||
|
138 | 138 | f.seek(0) |
|
139 | 139 | self._index = 0 |
|
140 | 140 | |
|
141 | def makev1commandrequest(ui, requestbuilder, caps, capablefn, | |
|
142 | repobaseurl, cmd, args): | |
|
143 | """Make an HTTP request to run a command for a version 1 client. | |
|
144 | ||
|
145 | ``caps`` is a set of known server capabilities. The value may be | |
|
146 | None if capabilities are not yet known. | |
|
147 | ||
|
148 | ``capablefn`` is a function to evaluate a capability. | |
|
149 | ||
|
150 | ``cmd``, ``args``, and ``data`` define the command, its arguments, and | |
|
151 | raw data to pass to it. | |
|
152 | """ | |
|
153 | if cmd == 'pushkey': | |
|
154 | args['data'] = '' | |
|
155 | data = args.pop('data', None) | |
|
156 | headers = args.pop('headers', {}) | |
|
157 | ||
|
158 | ui.debug("sending %s command\n" % cmd) | |
|
159 | q = [('cmd', cmd)] | |
|
160 | headersize = 0 | |
|
161 | varyheaders = [] | |
|
162 | # Important: don't use self.capable() here or else you end up | |
|
163 | # with infinite recursion when trying to look up capabilities | |
|
164 | # for the first time. | |
|
165 | postargsok = caps is not None and 'httppostargs' in caps | |
|
166 | ||
|
167 | # Send arguments via POST. | |
|
168 | if postargsok and args: | |
|
169 | strargs = urlreq.urlencode(sorted(args.items())) | |
|
170 | if not data: | |
|
171 | data = strargs | |
|
172 | else: | |
|
173 | if isinstance(data, bytes): | |
|
174 | i = io.BytesIO(data) | |
|
175 | i.length = len(data) | |
|
176 | data = i | |
|
177 | argsio = io.BytesIO(strargs) | |
|
178 | argsio.length = len(strargs) | |
|
179 | data = _multifile(argsio, data) | |
|
180 | headers[r'X-HgArgs-Post'] = len(strargs) | |
|
181 | elif args: | |
|
182 | # Calling self.capable() can infinite loop if we are calling | |
|
183 | # "capabilities". But that command should never accept wire | |
|
184 | # protocol arguments. So this should never happen. | |
|
185 | assert cmd != 'capabilities' | |
|
186 | httpheader = capablefn('httpheader') | |
|
187 | if httpheader: | |
|
188 | headersize = int(httpheader.split(',', 1)[0]) | |
|
189 | ||
|
190 | # Send arguments via HTTP headers. | |
|
191 | if headersize > 0: | |
|
192 | # The headers can typically carry more data than the URL. | |
|
193 | encargs = urlreq.urlencode(sorted(args.items())) | |
|
194 | for header, value in encodevalueinheaders(encargs, 'X-HgArg', | |
|
195 | headersize): | |
|
196 | headers[header] = value | |
|
197 | varyheaders.append(header) | |
|
198 | # Send arguments via query string (Mercurial <1.9). | |
|
199 | else: | |
|
200 | q += sorted(args.items()) | |
|
201 | ||
|
202 | qs = '?%s' % urlreq.urlencode(q) | |
|
203 | cu = "%s%s" % (repobaseurl, qs) | |
|
204 | size = 0 | |
|
205 | if util.safehasattr(data, 'length'): | |
|
206 | size = data.length | |
|
207 | elif data is not None: | |
|
208 | size = len(data) | |
|
209 | if data is not None and r'Content-Type' not in headers: | |
|
210 | headers[r'Content-Type'] = r'application/mercurial-0.1' | |
|
211 | ||
|
212 | # Tell the server we accept application/mercurial-0.2 and multiple | |
|
213 | # compression formats if the server is capable of emitting those | |
|
214 | # payloads. | |
|
215 | protoparams = {'partial-pull'} | |
|
216 | ||
|
217 | mediatypes = set() | |
|
218 | if caps is not None: | |
|
219 | mt = capablefn('httpmediatype') | |
|
220 | if mt: | |
|
221 | protoparams.add('0.1') | |
|
222 | mediatypes = set(mt.split(',')) | |
|
223 | ||
|
224 | if '0.2tx' in mediatypes: | |
|
225 | protoparams.add('0.2') | |
|
226 | ||
|
227 | if '0.2tx' in mediatypes and capablefn('compression'): | |
|
228 | # We /could/ compare supported compression formats and prune | |
|
229 | # non-mutually supported or error if nothing is mutually supported. | |
|
230 | # For now, send the full list to the server and have it error. | |
|
231 | comps = [e.wireprotosupport().name for e in | |
|
232 | util.compengines.supportedwireengines(util.CLIENTROLE)] | |
|
233 | protoparams.add('comp=%s' % ','.join(comps)) | |
|
234 | ||
|
235 | if protoparams: | |
|
236 | protoheaders = encodevalueinheaders(' '.join(sorted(protoparams)), | |
|
237 | 'X-HgProto', | |
|
238 | headersize or 1024) | |
|
239 | for header, value in protoheaders: | |
|
240 | headers[header] = value | |
|
241 | varyheaders.append(header) | |
|
242 | ||
|
243 | if varyheaders: | |
|
244 | headers[r'Vary'] = r','.join(varyheaders) | |
|
245 | ||
|
246 | req = requestbuilder(pycompat.strurl(cu), data, headers) | |
|
247 | ||
|
248 | if data is not None: | |
|
249 | ui.debug("sending %d bytes\n" % size) | |
|
250 | req.add_unredirected_header(r'Content-Length', r'%d' % size) | |
|
251 | ||
|
252 | return req, cu, qs | |
|
253 | ||
|
141 | 254 | def sendrequest(ui, opener, req): |
|
142 | 255 | """Send a prepared HTTP request. |
|
143 | 256 | |
@@ -228,104 +341,11 b' class httppeer(wireproto.wirepeer):' | |||
|
228 | 341 | |
|
229 | 342 | def _callstream(self, cmd, _compressible=False, **args): |
|
230 | 343 | args = pycompat.byteskwargs(args) |
|
231 | if cmd == 'pushkey': | |
|
232 | args['data'] = '' | |
|
233 | data = args.pop('data', None) | |
|
234 | headers = args.pop('headers', {}) | |
|
235 | ||
|
236 | self.ui.debug("sending %s command\n" % cmd) | |
|
237 | q = [('cmd', cmd)] | |
|
238 | headersize = 0 | |
|
239 | varyheaders = [] | |
|
240 | # Important: don't use self.capable() here or else you end up | |
|
241 | # with infinite recursion when trying to look up capabilities | |
|
242 | # for the first time. | |
|
243 | postargsok = self._caps is not None and 'httppostargs' in self._caps | |
|
244 | ||
|
245 | # Send arguments via POST. | |
|
246 | if postargsok and args: | |
|
247 | strargs = urlreq.urlencode(sorted(args.items())) | |
|
248 | if not data: | |
|
249 | data = strargs | |
|
250 | else: | |
|
251 | if isinstance(data, bytes): | |
|
252 | i = io.BytesIO(data) | |
|
253 | i.length = len(data) | |
|
254 | data = i | |
|
255 | argsio = io.BytesIO(strargs) | |
|
256 | argsio.length = len(strargs) | |
|
257 | data = _multifile(argsio, data) | |
|
258 | headers[r'X-HgArgs-Post'] = len(strargs) | |
|
259 | elif args: | |
|
260 | # Calling self.capable() can infinite loop if we are calling | |
|
261 | # "capabilities". But that command should never accept wire | |
|
262 | # protocol arguments. So this should never happen. | |
|
263 | assert cmd != 'capabilities' | |
|
264 | httpheader = self.capable('httpheader') | |
|
265 | if httpheader: | |
|
266 | headersize = int(httpheader.split(',', 1)[0]) | |
|
267 | ||
|
268 | # Send arguments via HTTP headers. | |
|
269 | if headersize > 0: | |
|
270 | # The headers can typically carry more data than the URL. | |
|
271 | encargs = urlreq.urlencode(sorted(args.items())) | |
|
272 | for header, value in encodevalueinheaders(encargs, 'X-HgArg', | |
|
273 | headersize): | |
|
274 | headers[header] = value | |
|
275 | varyheaders.append(header) | |
|
276 | # Send arguments via query string (Mercurial <1.9). | |
|
277 | else: | |
|
278 | q += sorted(args.items()) | |
|
279 | 344 | |
|
280 | qs = '?%s' % urlreq.urlencode(q) | |
|
281 | cu = "%s%s" % (self._url, qs) | |
|
282 | size = 0 | |
|
283 | if util.safehasattr(data, 'length'): | |
|
284 | size = data.length | |
|
285 | elif data is not None: | |
|
286 | size = len(data) | |
|
287 | if data is not None and r'Content-Type' not in headers: | |
|
288 | headers[r'Content-Type'] = r'application/mercurial-0.1' | |
|
289 | ||
|
290 | # Tell the server we accept application/mercurial-0.2 and multiple | |
|
291 | # compression formats if the server is capable of emitting those | |
|
292 | # payloads. | |
|
293 | protoparams = {'partial-pull'} | |
|
294 | ||
|
295 | mediatypes = set() | |
|
296 | if self._caps is not None: | |
|
297 | mt = self.capable('httpmediatype') | |
|
298 | if mt: | |
|
299 | protoparams.add('0.1') | |
|
300 | mediatypes = set(mt.split(',')) | |
|
301 | ||
|
302 | if '0.2tx' in mediatypes: | |
|
303 | protoparams.add('0.2') | |
|
345 | req, cu, qs = makev1commandrequest(self.ui, self._requestbuilder, | |
|
346 | self._caps, self.capable, | |
|
347 | self._url, cmd, args) | |
|
304 | 348 | |
|
305 | if '0.2tx' in mediatypes and self.capable('compression'): | |
|
306 | # We /could/ compare supported compression formats and prune | |
|
307 | # non-mutually supported or error if nothing is mutually supported. | |
|
308 | # For now, send the full list to the server and have it error. | |
|
309 | comps = [e.wireprotosupport().name for e in | |
|
310 | util.compengines.supportedwireengines(util.CLIENTROLE)] | |
|
311 | protoparams.add('comp=%s' % ','.join(comps)) | |
|
312 | ||
|
313 | if protoparams: | |
|
314 | protoheaders = encodevalueinheaders(' '.join(sorted(protoparams)), | |
|
315 | 'X-HgProto', | |
|
316 | headersize or 1024) | |
|
317 | for header, value in protoheaders: | |
|
318 | headers[header] = value | |
|
319 | varyheaders.append(header) | |
|
320 | ||
|
321 | if varyheaders: | |
|
322 | headers[r'Vary'] = r','.join(varyheaders) | |
|
323 | ||
|
324 | req = self._requestbuilder(pycompat.strurl(cu), data, headers) | |
|
325 | ||
|
326 | if data is not None: | |
|
327 | self.ui.debug("sending %d bytes\n" % size) | |
|
328 | req.add_unredirected_header(r'Content-Length', r'%d' % size) | |
|
329 | 349 | try: |
|
330 | 350 | resp = sendrequest(self.ui, self._urlopener, req) |
|
331 | 351 | except urlerr.httperror as inst: |
General Comments 0
You need to be logged in to leave comments.
Login now