comparison mercurial/httppeer.py @ 37551:946eb204ba67

httppeer: extract common response handling into own function This allows the common redirect detection, content type validation, and decompression wrapping to be usable outside of httppeer instances. Differential Revision: https://phab.mercurial-scm.org/D3236
author Gregory Szorc <gregory.szorc@gmail.com>
date Tue, 10 Apr 2018 12:52:29 -0700
parents b5862ee01abe
children 8b8a845c85fc
comparison
equal deleted inserted replaced
37550:b5862ee01abe 37551:946eb204ba67
303 # Insert error handlers for common I/O failures. 303 # Insert error handlers for common I/O failures.
304 _wraphttpresponse(res) 304 _wraphttpresponse(res)
305 305
306 return res 306 return res
307 307
308 def parsev1commandresponse(ui, baseurl, requrl, qs, resp, compressible):
309 # record the url we got redirected to
310 respurl = pycompat.bytesurl(resp.geturl())
311 if respurl.endswith(qs):
312 respurl = respurl[:-len(qs)]
313 if baseurl.rstrip('/') != respurl.rstrip('/'):
314 if not ui.quiet:
315 ui.warn(_('real URL is %s\n') % respurl)
316
317 try:
318 proto = pycompat.bytesurl(resp.getheader(r'content-type', r''))
319 except AttributeError:
320 proto = pycompat.bytesurl(resp.headers.get(r'content-type', r''))
321
322 safeurl = util.hidepassword(baseurl)
323 if proto.startswith('application/hg-error'):
324 raise error.OutOfBandError(resp.read())
325 # accept old "text/plain" and "application/hg-changegroup" for now
326 if not (proto.startswith('application/mercurial-') or
327 (proto.startswith('text/plain')
328 and not resp.headers.get('content-length')) or
329 proto.startswith('application/hg-changegroup')):
330 ui.debug("requested URL: '%s'\n" % util.hidepassword(requrl))
331 raise error.RepoError(
332 _("'%s' does not appear to be an hg repository:\n"
333 "---%%<--- (%s)\n%s\n---%%<---\n")
334 % (safeurl, proto or 'no content-type', resp.read(1024)))
335
336 if proto.startswith('application/mercurial-'):
337 try:
338 version = proto.split('-', 1)[1]
339 version_info = tuple([int(n) for n in version.split('.')])
340 except ValueError:
341 raise error.RepoError(_("'%s' sent a broken Content-Type "
342 "header (%s)") % (safeurl, proto))
343
344 # TODO consider switching to a decompression reader that uses
345 # generators.
346 if version_info == (0, 1):
347 if compressible:
348 resp = util.compengines['zlib'].decompressorreader(resp)
349
350 return respurl, resp
351
352 elif version_info == (0, 2):
353 # application/mercurial-0.2 always identifies the compression
354 # engine in the payload header.
355 elen = struct.unpack('B', resp.read(1))[0]
356 ename = resp.read(elen)
357 engine = util.compengines.forwiretype(ename)
358 return respurl, engine.decompressorreader(resp)
359 else:
360 raise error.RepoError(_("'%s' uses newer protocol %s") %
361 (safeurl, version))
362
363 if compressible:
364 resp = util.compengines['zlib'].decompressorreader(resp)
365
366 return respurl, resp
367
308 class httppeer(wireproto.wirepeer): 368 class httppeer(wireproto.wirepeer):
309 def __init__(self, ui, path, url, opener, requestbuilder): 369 def __init__(self, ui, path, url, opener, requestbuilder):
310 self.ui = ui 370 self.ui = ui
311 self._path = path 371 self._path = path
312 self._url = url 372 self._url = url
360 self._caps, self.capable, 420 self._caps, self.capable,
361 self._url, cmd, args) 421 self._url, cmd, args)
362 422
363 resp = sendrequest(self.ui, self._urlopener, req) 423 resp = sendrequest(self.ui, self._urlopener, req)
364 424
365 # record the url we got redirected to 425 self._url, resp = parsev1commandresponse(self.ui, self._url, cu, qs,
366 resp_url = pycompat.bytesurl(resp.geturl()) 426 resp, _compressible)
367 if resp_url.endswith(qs):
368 resp_url = resp_url[:-len(qs)]
369 if self._url.rstrip('/') != resp_url.rstrip('/'):
370 if not self.ui.quiet:
371 self.ui.warn(_('real URL is %s\n') % resp_url)
372 self._url = resp_url
373 try:
374 proto = pycompat.bytesurl(resp.getheader(r'content-type', r''))
375 except AttributeError:
376 proto = pycompat.bytesurl(resp.headers.get(r'content-type', r''))
377
378 safeurl = util.hidepassword(self._url)
379 if proto.startswith('application/hg-error'):
380 raise error.OutOfBandError(resp.read())
381 # accept old "text/plain" and "application/hg-changegroup" for now
382 if not (proto.startswith('application/mercurial-') or
383 (proto.startswith('text/plain')
384 and not resp.headers.get('content-length')) or
385 proto.startswith('application/hg-changegroup')):
386 self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu))
387 raise error.RepoError(
388 _("'%s' does not appear to be an hg repository:\n"
389 "---%%<--- (%s)\n%s\n---%%<---\n")
390 % (safeurl, proto or 'no content-type', resp.read(1024)))
391
392 if proto.startswith('application/mercurial-'):
393 try:
394 version = proto.split('-', 1)[1]
395 version_info = tuple([int(n) for n in version.split('.')])
396 except ValueError:
397 raise error.RepoError(_("'%s' sent a broken Content-Type "
398 "header (%s)") % (safeurl, proto))
399
400 # TODO consider switching to a decompression reader that uses
401 # generators.
402 if version_info == (0, 1):
403 if _compressible:
404 return util.compengines['zlib'].decompressorreader(resp)
405 return resp
406 elif version_info == (0, 2):
407 # application/mercurial-0.2 always identifies the compression
408 # engine in the payload header.
409 elen = struct.unpack('B', resp.read(1))[0]
410 ename = resp.read(elen)
411 engine = util.compengines.forwiretype(ename)
412 return engine.decompressorreader(resp)
413 else:
414 raise error.RepoError(_("'%s' uses newer protocol %s") %
415 (safeurl, version))
416
417 if _compressible:
418 return util.compengines['zlib'].decompressorreader(resp)
419 427
420 return resp 428 return resp
421 429
422 def _call(self, cmd, **args): 430 def _call(self, cmd, **args):
423 fp = self._callstream(cmd, **args) 431 fp = self._callstream(cmd, **args)