##// END OF EJS Templates
exchange: backout changeset c26335fa4225...
exchange: backout changeset c26335fa4225 Changeset c26335fa4225 has good intends but introduce significant behavior regressions for multiple important cases. In short there are many case where push would have caught instability creation/propagation that are no longer covered. These behavior have been covered for many years and even if some related case are not currently caught, the covered one should not be regressed. The next four changesets introduce tests for some of these cases. However we could produce many more tests cases since the area is wide and they are many possible combination. (And we should cover them when getting back to this issue) Since 5.5 is one week away, the most reasonable approach seems to back this out while we devise a new way to move forward that preserve the current behavior, catch more issues and also improves the situation that c26335fa4225 target. In addition to the behavior change, c26335fa4225 also introduced output changes. These output changes does not requires a backout per-se, but are part of the same changeset. However they come with a couple of issues that also requires attention: 1) the bulk of the error message have been shoehorned into a multiple line abort message. This seems quite different from what we usually do. The abort message should be a compact and efficient message, with extra details being issued as normal error output beforehand. (with --verbose/--quiet) support. 2) the current output is unbounded, so if there is many (tens, hundreds, thousands, …) of unstable/obsolete changeset involved in the push, the output can quickly become a scary and un-usuable wall of text. So we need some limitation here (same as we have with the remote head list that says A, B , C and # others).

File last commit:

r43812:2fe6121c default
r45782:6063c185 stable
Show More
wireprotov2peer.py
576 lines | 18.1 KiB | text/x-python | PythonLexer
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 # wireprotov2peer.py - client side code for wire protocol version 2
#
# Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 import threading
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 from .i18n import _
from . import (
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739 encoding,
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 error,
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 pycompat,
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 sslutil,
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 url as urlmod,
util,
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 wireprotoframing,
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 wireprototypes,
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 )
Augie Fackler
formatting: blacken the codebase...
r43346 from .utils import cborutil
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 def formatrichmessage(atoms):
"""Format an encoded message from the framing protocol."""
chunks = []
for atom in atoms:
msg = _(atom[b'msg'])
if b'args' in atom:
Gregory Szorc
wireprotov2peer: properly format errors...
r39522 msg = msg % tuple(atom[b'args'])
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743
chunks.append(msg)
return b''.join(chunks)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 SUPPORTED_REDIRECT_PROTOCOLS = {
b'http',
b'https',
}
SUPPORTED_CONTENT_HASHES = {
b'sha1',
b'sha256',
}
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 def redirecttargetsupported(ui, target):
"""Determine whether a redirect target entry is supported.
``target`` should come from the capabilities data structure emitted by
the server.
"""
if target.get(b'protocol') not in SUPPORTED_REDIRECT_PROTOCOLS:
Augie Fackler
formatting: blacken the codebase...
r43346 ui.note(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'(remote redirect target %s uses unsupported protocol: %s)\n')
Augie Fackler
formatting: blacken the codebase...
r43346 % (target[b'name'], target.get(b'protocol', b''))
)
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 return False
if target.get(b'snirequired') and not sslutil.hassni:
Augie Fackler
formatting: blacken the codebase...
r43346 ui.note(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'(redirect target %s requires SNI, which is unsupported)\n')
Augie Fackler
formatting: blacken the codebase...
r43346 % target[b'name']
)
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 return False
if b'tlsversions' in target:
tlsversions = set(target[b'tlsversions'])
supported = set()
for v in sslutil.supportedprotocols:
assert v.startswith(b'tls')
supported.add(v[3:])
if not tlsversions & supported:
Augie Fackler
formatting: blacken the codebase...
r43346 ui.note(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'(remote redirect target %s requires unsupported TLS '
b'versions: %s)\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
% (target[b'name'], b', '.join(sorted(tlsversions)))
)
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 return False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.note(_(b'(remote redirect target %s is compatible)\n') % target[b'name'])
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060
return True
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 def supportedredirects(ui, apidescriptor):
"""Resolve the "redirect" command request key given an API descriptor.
Given an API descriptor returned by the server, returns a data structure
that can be used in hte "redirect" field of command requests to advertise
support for compatible redirect targets.
Returns None if no redirect targets are remotely advertised or if none are
supported.
"""
if not apidescriptor or b'redirect' not in apidescriptor:
return None
Augie Fackler
formatting: blacken the codebase...
r43346 targets = [
t[b'name']
for t in apidescriptor[b'redirect'][b'targets']
if redirecttargetsupported(ui, t)
]
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060
Augie Fackler
formatting: blacken the codebase...
r43346 hashes = [
h
for h in apidescriptor[b'redirect'][b'hashes']
if h in SUPPORTED_CONTENT_HASHES
]
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060
return {
b'targets': targets,
b'hashes': hashes,
}
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738 class commandresponse(object):
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 """Represents the response to a command request.
Instances track the state of the command and hold its results.
An external entity is required to update the state of the object when
events occur.
"""
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 def __init__(self, requestid, command, fromredirect=False):
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738 self.requestid = requestid
self.command = command
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 self.fromredirect = fromredirect
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 # Whether all remote input related to this command has been
# received.
self._inputcomplete = False
# We have a lock that is acquired when important object state is
# mutated. This is to prevent race conditions between 1 thread
# sending us new data and another consuming it.
self._lock = threading.RLock()
# An event is set when state of the object changes. This event
# is waited on by the generator emitting objects.
self._serviceable = threading.Event()
self._pendingevents = []
Gregory Szorc
wireprotov2: raise exception in objects() if future has been resolved...
r40172 self._pendingerror = None
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 self._decoder = cborutil.bufferingdecoder()
self._seeninitial = False
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 self._redirect = None
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597
def _oninputcomplete(self):
with self._lock:
self._inputcomplete = True
self._serviceable.set()
def _onresponsedata(self, data):
available, readcount, wanted = self._decoder.decode(data)
if not available:
return
with self._lock:
for o in self._decoder.getavailable():
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 if not self._seeninitial and not self.fromredirect:
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 self._handleinitial(o)
continue
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 # We should never see an object after a content redirect,
# as the spec says the main status object containing the
# content redirect is the only object in the stream. Fail
# if we see a misbehaving server.
if self._redirect:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'received unexpected response data '
b'after content redirect; the remote is '
b'buggy'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 self._pendingevents.append(o)
self._serviceable.set()
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738
Gregory Szorc
wireprotov2: raise exception in objects() if future has been resolved...
r40172 def _onerror(self, e):
self._pendingerror = e
with self._lock:
self._serviceable.set()
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 def _handleinitial(self, o):
self._seeninitial = True
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 if o[b'status'] == b'ok':
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 return
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 elif o[b'status'] == b'redirect':
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 l = o[b'location']
self._redirect = wireprototypes.alternatelocationresponse(
url=l[b'url'],
mediatype=l[b'mediatype'],
size=l.get(b'size'),
fullhashes=l.get(b'fullhashes'),
fullhashseed=l.get(b'fullhashseed'),
serverdercerts=l.get(b'serverdercerts'),
Augie Fackler
formatting: blacken the codebase...
r43346 servercadercerts=l.get(b'servercadercerts'),
)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 return
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 atoms = [{b'msg': o[b'error'][b'message']}]
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 if b'args' in o[b'error']:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 atoms[0][b'args'] = o[b'error'][b'args']
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597
raise error.RepoError(formatrichmessage(atoms))
def objects(self):
"""Obtained decoded objects from this response.
This is a generator of data structures that were decoded from the
command response.
Obtaining the next member of the generator may block due to waiting
on external data to become available.
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 If the server encountered an error in the middle of serving the data
or if another error occurred, an exception may be raised when
advancing the generator.
"""
while True:
# TODO this can infinite loop if self._inputcomplete is never
# set. We likely want to tie the lifetime of this object/state
# to that of the background thread receiving frames and updating
# our state.
self._serviceable.wait(1.0)
Gregory Szorc
wireprotov2: raise exception in objects() if future has been resolved...
r40172 if self._pendingerror:
raise self._pendingerror
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 with self._lock:
self._serviceable.clear()
# Make copies because objects could be mutated during
# iteration.
stop = self._inputcomplete
pending = list(self._pendingevents)
self._pendingevents[:] = []
for o in pending:
yield o
if stop:
break
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 class clienthandler(object):
"""Object to handle higher-level client activities.
The ``clientreactor`` is used to hold low-level state about the frame-based
protocol, such as which requests and streams are active. This type is used
for higher-level operations, such as reading frames from a socket, exposing
and managing a higher-level primitive for representing command responses,
etc. This class is what peers should probably use to bridge wire activity
with the higher-level peer API.
"""
Augie Fackler
formatting: blacken the codebase...
r43346 def __init__(
self, ui, clientreactor, opener=None, requestbuilder=util.urlreq.request
):
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 self._ui = ui
self._reactor = clientreactor
self._requests = {}
self._futures = {}
self._responses = {}
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 self._redirects = []
Gregory Szorc
wireprotov2: change name and behavior of readframe()...
r40055 self._frameseof = False
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 self._opener = opener or urlmod.opener(ui)
self._requestbuilder = requestbuilder
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Gregory Szorc
wireprotov2: client support for advertising redirect targets...
r40060 def callcommand(self, command, args, f, redirect=None):
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 """Register a request to call a command.
Returns an iterable of frames that should be sent over the wire.
"""
Augie Fackler
formatting: blacken the codebase...
r43346 request, action, meta = self._reactor.callcommand(
command, args, redirect=redirect
)
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if action != b'noop':
raise error.ProgrammingError(b'%s not yet supported' % action)
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
rid = request.requestid
self._requests[rid] = request
self._futures[rid] = f
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 # TODO we need some kind of lifetime on response instances otherwise
# objects() may deadlock.
Gregory Szorc
wireprotov2: establish a type for representing command response...
r37738 self._responses[rid] = commandresponse(rid, command)
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
return iter(())
def flushcommands(self):
"""Flush all queued commands.
Returns an iterable of frames that should be sent over the wire.
"""
action, meta = self._reactor.flushcommands()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if action != b'sendframes':
raise error.ProgrammingError(b'%s not yet supported' % action)
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return meta[b'framegen']
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Gregory Szorc
wireprotov2: change name and behavior of readframe()...
r40055 def readdata(self, framefh):
"""Attempt to read data and do work.
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Gregory Szorc
wireprotov2: change name and behavior of readframe()...
r40055 Returns None if no data was read. Presumably this means we're
done with all read I/O.
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 """
Gregory Szorc
wireprotov2: change name and behavior of readframe()...
r40055 if not self._frameseof:
frame = wireprotoframing.readframe(framefh)
if frame is None:
# TODO tell reactor?
self._frameseof = True
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._ui.debug(b'received %r\n' % frame)
Gregory Szorc
wireprotov2: change name and behavior of readframe()...
r40055 self._processframe(frame)
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 # Also try to read the first redirect.
if self._redirects:
if not self._processredirect(*self._redirects[0]):
self._redirects.pop(0)
if self._frameseof and not self._redirects:
Gregory Szorc
wireprotov2: change name and behavior of readframe()...
r40055 return None
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
return True
def _processframe(self, frame):
"""Process a single read frame."""
action, meta = self._reactor.onframerecv(frame)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if action == b'error':
e = error.RepoError(meta[b'message'])
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 if frame.requestid in self._responses:
self._responses[frame.requestid]._oninputcomplete()
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 if frame.requestid in self._futures:
self._futures[frame.requestid].set_exception(e)
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 del self._futures[frame.requestid]
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 else:
raise e
Gregory Szorc
wireprotov2: implement commands as a generator of objects...
r39595 return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif action == b'noop':
Gregory Szorc
wireprotov2: handle noop action...
r40169 return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif action == b'responsedata':
Gregory Szorc
wireprotov2: raise ProgrammingError on unknown action...
r40174 # Handled below.
pass
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'action not handled: %s' % action)
Gregory Szorc
wireprotov2: implement commands as a generator of objects...
r39595
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 if frame.requestid not in self._requests:
raise error.ProgrammingError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'received frame for unknown request; this is either a bug in '
b'the clientreactor not screening for this or this instance was '
b'never told about this request: %r' % frame
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737
response = self._responses[frame.requestid]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if action == b'responsedata':
Gregory Szorc
wireprotov2peer: report exceptions in frame handling against request future...
r39521 # Any failures processing this frame should bubble up to the
# future tracking the request.
try:
self._processresponsedata(frame, meta, response)
except BaseException as e:
Gregory Szorc
wireprotov2: raise exception in objects() if future has been resolved...
r40172 # If an exception occurs before the future is resolved,
# fail the future. Otherwise, we stuff the exception on
# the response object so it can be raised during objects()
# iteration. If nothing is consuming objects(), we could
# silently swallow this exception. That's a risk we'll have to
# take.
if frame.requestid in self._futures:
self._futures[frame.requestid].set_exception(e)
del self._futures[frame.requestid]
response._oninputcomplete()
else:
response._onerror(e)
Gregory Szorc
wireprotov2: move response handling out of httppeer...
r37737 else:
raise error.ProgrammingError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'unhandled action from clientreactor: %s' % action
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
Gregory Szorc
wireprotov2peer: split responsedata handling into separate function...
r39469 def _processresponsedata(self, frame, meta, response):
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 # This can raise. The caller can handle it.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 response._onresponsedata(meta[b'data'])
Gregory Szorc
wireprotov2peer: split responsedata handling into separate function...
r39469
Gregory Szorc
wireprotov2peer: wait for initial object before resolving future...
r40790 # We need to be careful about resolving futures prematurely. If a
# response is a redirect response, resolving the future before the
# redirect is processed would result in the consumer seeing an
# empty stream of objects, since they'd be consuming our
# response.objects() instead of the redirect's response.objects().
#
# Our strategy is to not resolve/finish the request until either
# EOS occurs or until the initial response object is fully received.
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
Gregory Szorc
wireprotov2peer: wait for initial object before resolving future...
r40790 # Always react to eos.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if meta[b'eos']:
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 response._oninputcomplete()
del self._requests[frame.requestid]
Gregory Szorc
wireprotov2peer: split responsedata handling into separate function...
r39469
Gregory Szorc
wireprotov2peer: wait for initial object before resolving future...
r40790 # Not EOS but we haven't decoded the initial response object yet.
# Return and wait for more data.
elif not response._seeninitial:
return
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
Gregory Szorc
wireprotov2peer: wait for initial object before resolving future...
r40790 # The specification says no objects should follow the initial/redirect
# object. So it should be safe to handle the redirect object if one is
# decoded, without having to wait for EOS.
if response._redirect:
self._followredirect(frame.requestid, response._redirect)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 return
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 # If the command has a decoder, we wait until all input has been
# received before resolving the future. Otherwise we resolve the
# future immediately.
if frame.requestid not in self._futures:
return
Gregory Szorc
wireprotov2peer: split responsedata handling into separate function...
r39469
Gregory Szorc
wireprotov2peer: stream decoded responses...
r39597 if response.command not in COMMAND_DECODERS:
self._futures[frame.requestid].set_result(response.objects())
del self._futures[frame.requestid]
elif response._inputcomplete:
decoded = COMMAND_DECODERS[response.command](response.objects())
self._futures[frame.requestid].set_result(decoded)
Gregory Szorc
wireprotov2peer: split responsedata handling into separate function...
r39469 del self._futures[frame.requestid]
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 def _followredirect(self, requestid, redirect):
"""Called to initiate redirect following for a request."""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._ui.note(_(b'(following redirect to %s)\n') % redirect.url)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
# TODO handle framed responses.
if redirect.mediatype != b'application/mercurial-cbor':
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'cannot handle redirects for the %s media type')
Augie Fackler
formatting: blacken the codebase...
r43346 % redirect.mediatype
)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
if redirect.fullhashes:
Augie Fackler
formatting: blacken the codebase...
r43346 self._ui.warn(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'(support for validating hashes on content '
b'redirects not supported)\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
if redirect.serverdercerts or redirect.servercadercerts:
Augie Fackler
formatting: blacken the codebase...
r43346 self._ui.warn(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'(support for pinning server certificates on '
b'content redirects not supported)\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
headers = {
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 'Accept': redirect.mediatype,
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 }
req = self._requestbuilder(pycompat.strurl(redirect.url), None, headers)
try:
res = self._opener.open(req)
except util.urlerr.httperror as e:
if e.code == 401:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'authorization failed'))
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 raise
except util.httplib.HTTPException as e:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._ui.debug(b'http error requesting %s\n' % req.get_full_url())
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 self._ui.traceback()
raise IOError(None, e)
urlmod.wrapresponse(res)
# The existing response object is associated with frame data. Rather
# than try to normalize its state, just create a new object.
oldresponse = self._responses[requestid]
Augie Fackler
formatting: blacken the codebase...
r43346 self._responses[requestid] = commandresponse(
requestid, oldresponse.command, fromredirect=True
)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
self._redirects.append((requestid, res))
def _processredirect(self, rid, res):
Gregory Szorc
wireprotov2peer: always return a bool from _processredirect()...
r40789 """Called to continue processing a response from a redirect.
Returns a bool indicating if the redirect is still serviceable.
"""
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062 response = self._responses[rid]
try:
data = res.read(32768)
response._onresponsedata(data)
# We're at end of stream.
if not data:
response._oninputcomplete()
if rid not in self._futures:
Gregory Szorc
wireprotov2peer: always return a bool from _processredirect()...
r40789 return bool(data)
Gregory Szorc
wireprotov2: client support for following content redirects...
r40062
if response.command not in COMMAND_DECODERS:
self._futures[rid].set_result(response.objects())
del self._futures[rid]
elif response._inputcomplete:
decoded = COMMAND_DECODERS[response.command](response.objects())
self._futures[rid].set_result(decoded)
del self._futures[rid]
return bool(data)
except BaseException as e:
self._futures[rid].set_exception(e)
del self._futures[rid]
response._oninputcomplete()
return False
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 def decodebranchmap(objs):
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739 # Response should be a single CBOR map of branch name to array of nodes.
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 bm = next(objs)
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
return {encoding.tolocal(k): v for k, v in bm.items()}
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 def decodeheads(objs):
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739 # Array of node bytestrings.
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 return next(objs)
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 def decodeknown(objs):
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739 # Bytestring where each byte is a 0 or 1.
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 raw = next(objs)
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
Augie Fackler
formatting: blacken the codebase...
r43346 return [True if raw[i : i + 1] == b'1' else False for i in range(len(raw))]
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 def decodelistkeys(objs):
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739 # Map with bytestring keys and values.
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 return next(objs)
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 def decodelookup(objs):
return next(objs)
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: change command response protocol to include a leading map...
r37743 def decodepushkey(objs):
return next(objs)
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739 COMMAND_DECODERS = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'branchmap': decodebranchmap,
b'heads': decodeheads,
b'known': decodeknown,
b'listkeys': decodelistkeys,
b'lookup': decodelookup,
b'pushkey': decodepushkey,
Gregory Szorc
wireprotov2: decode responses to their expected types...
r37739 }