mercurial/wireprotov2peer.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43506 9f70512ae2cf
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    56     ``target`` should come from the capabilities data structure emitted by
    56     ``target`` should come from the capabilities data structure emitted by
    57     the server.
    57     the server.
    58     """
    58     """
    59     if target.get(b'protocol') not in SUPPORTED_REDIRECT_PROTOCOLS:
    59     if target.get(b'protocol') not in SUPPORTED_REDIRECT_PROTOCOLS:
    60         ui.note(
    60         ui.note(
    61             _('(remote redirect target %s uses unsupported protocol: %s)\n')
    61             _(b'(remote redirect target %s uses unsupported protocol: %s)\n')
    62             % (target[b'name'], target.get(b'protocol', b''))
    62             % (target[b'name'], target.get(b'protocol', b''))
    63         )
    63         )
    64         return False
    64         return False
    65 
    65 
    66     if target.get(b'snirequired') and not sslutil.hassni:
    66     if target.get(b'snirequired') and not sslutil.hassni:
    67         ui.note(
    67         ui.note(
    68             _('(redirect target %s requires SNI, which is unsupported)\n')
    68             _(b'(redirect target %s requires SNI, which is unsupported)\n')
    69             % target[b'name']
    69             % target[b'name']
    70         )
    70         )
    71         return False
    71         return False
    72 
    72 
    73     if b'tlsversions' in target:
    73     if b'tlsversions' in target:
    79             supported.add(v[3:])
    79             supported.add(v[3:])
    80 
    80 
    81         if not tlsversions & supported:
    81         if not tlsversions & supported:
    82             ui.note(
    82             ui.note(
    83                 _(
    83                 _(
    84                     '(remote redirect target %s requires unsupported TLS '
    84                     b'(remote redirect target %s requires unsupported TLS '
    85                     'versions: %s)\n'
    85                     b'versions: %s)\n'
    86                 )
    86                 )
    87                 % (target[b'name'], b', '.join(sorted(tlsversions)))
    87                 % (target[b'name'], b', '.join(sorted(tlsversions)))
    88             )
    88             )
    89             return False
    89             return False
    90 
    90 
    91     ui.note(_('(remote redirect target %s is compatible)\n') % target[b'name'])
    91     ui.note(_(b'(remote redirect target %s is compatible)\n') % target[b'name'])
    92 
    92 
    93     return True
    93     return True
    94 
    94 
    95 
    95 
    96 def supportedredirects(ui, apidescriptor):
    96 def supportedredirects(ui, apidescriptor):
   179                 # content redirect is the only object in the stream. Fail
   179                 # content redirect is the only object in the stream. Fail
   180                 # if we see a misbehaving server.
   180                 # if we see a misbehaving server.
   181                 if self._redirect:
   181                 if self._redirect:
   182                     raise error.Abort(
   182                     raise error.Abort(
   183                         _(
   183                         _(
   184                             'received unexpected response data '
   184                             b'received unexpected response data '
   185                             'after content redirect; the remote is '
   185                             b'after content redirect; the remote is '
   186                             'buggy'
   186                             b'buggy'
   187                         )
   187                         )
   188                     )
   188                     )
   189 
   189 
   190                 self._pendingevents.append(o)
   190                 self._pendingevents.append(o)
   191 
   191 
   213                 serverdercerts=l.get(b'serverdercerts'),
   213                 serverdercerts=l.get(b'serverdercerts'),
   214                 servercadercerts=l.get(b'servercadercerts'),
   214                 servercadercerts=l.get(b'servercadercerts'),
   215             )
   215             )
   216             return
   216             return
   217 
   217 
   218         atoms = [{'msg': o[b'error'][b'message']}]
   218         atoms = [{b'msg': o[b'error'][b'message']}]
   219         if b'args' in o[b'error']:
   219         if b'args' in o[b'error']:
   220             atoms[0]['args'] = o[b'error'][b'args']
   220             atoms[0][b'args'] = o[b'error'][b'args']
   221 
   221 
   222         raise error.RepoError(formatrichmessage(atoms))
   222         raise error.RepoError(formatrichmessage(atoms))
   223 
   223 
   224     def objects(self):
   224     def objects(self):
   225         """Obtained decoded objects from this response.
   225         """Obtained decoded objects from this response.
   291         """
   291         """
   292         request, action, meta = self._reactor.callcommand(
   292         request, action, meta = self._reactor.callcommand(
   293             command, args, redirect=redirect
   293             command, args, redirect=redirect
   294         )
   294         )
   295 
   295 
   296         if action != 'noop':
   296         if action != b'noop':
   297             raise error.ProgrammingError('%s not yet supported' % action)
   297             raise error.ProgrammingError(b'%s not yet supported' % action)
   298 
   298 
   299         rid = request.requestid
   299         rid = request.requestid
   300         self._requests[rid] = request
   300         self._requests[rid] = request
   301         self._futures[rid] = f
   301         self._futures[rid] = f
   302         # TODO we need some kind of lifetime on response instances otherwise
   302         # TODO we need some kind of lifetime on response instances otherwise
   310 
   310 
   311         Returns an iterable of frames that should be sent over the wire.
   311         Returns an iterable of frames that should be sent over the wire.
   312         """
   312         """
   313         action, meta = self._reactor.flushcommands()
   313         action, meta = self._reactor.flushcommands()
   314 
   314 
   315         if action != 'sendframes':
   315         if action != b'sendframes':
   316             raise error.ProgrammingError('%s not yet supported' % action)
   316             raise error.ProgrammingError(b'%s not yet supported' % action)
   317 
   317 
   318         return meta['framegen']
   318         return meta[b'framegen']
   319 
   319 
   320     def readdata(self, framefh):
   320     def readdata(self, framefh):
   321         """Attempt to read data and do work.
   321         """Attempt to read data and do work.
   322 
   322 
   323         Returns None if no data was read. Presumably this means we're
   323         Returns None if no data was read. Presumably this means we're
   327             frame = wireprotoframing.readframe(framefh)
   327             frame = wireprotoframing.readframe(framefh)
   328             if frame is None:
   328             if frame is None:
   329                 # TODO tell reactor?
   329                 # TODO tell reactor?
   330                 self._frameseof = True
   330                 self._frameseof = True
   331             else:
   331             else:
   332                 self._ui.debug('received %r\n' % frame)
   332                 self._ui.debug(b'received %r\n' % frame)
   333                 self._processframe(frame)
   333                 self._processframe(frame)
   334 
   334 
   335         # Also try to read the first redirect.
   335         # Also try to read the first redirect.
   336         if self._redirects:
   336         if self._redirects:
   337             if not self._processredirect(*self._redirects[0]):
   337             if not self._processredirect(*self._redirects[0]):
   345     def _processframe(self, frame):
   345     def _processframe(self, frame):
   346         """Process a single read frame."""
   346         """Process a single read frame."""
   347 
   347 
   348         action, meta = self._reactor.onframerecv(frame)
   348         action, meta = self._reactor.onframerecv(frame)
   349 
   349 
   350         if action == 'error':
   350         if action == b'error':
   351             e = error.RepoError(meta['message'])
   351             e = error.RepoError(meta[b'message'])
   352 
   352 
   353             if frame.requestid in self._responses:
   353             if frame.requestid in self._responses:
   354                 self._responses[frame.requestid]._oninputcomplete()
   354                 self._responses[frame.requestid]._oninputcomplete()
   355 
   355 
   356             if frame.requestid in self._futures:
   356             if frame.requestid in self._futures:
   358                 del self._futures[frame.requestid]
   358                 del self._futures[frame.requestid]
   359             else:
   359             else:
   360                 raise e
   360                 raise e
   361 
   361 
   362             return
   362             return
   363         elif action == 'noop':
   363         elif action == b'noop':
   364             return
   364             return
   365         elif action == 'responsedata':
   365         elif action == b'responsedata':
   366             # Handled below.
   366             # Handled below.
   367             pass
   367             pass
   368         else:
   368         else:
   369             raise error.ProgrammingError('action not handled: %s' % action)
   369             raise error.ProgrammingError(b'action not handled: %s' % action)
   370 
   370 
   371         if frame.requestid not in self._requests:
   371         if frame.requestid not in self._requests:
   372             raise error.ProgrammingError(
   372             raise error.ProgrammingError(
   373                 'received frame for unknown request; this is either a bug in '
   373                 b'received frame for unknown request; this is either a bug in '
   374                 'the clientreactor not screening for this or this instance was '
   374                 b'the clientreactor not screening for this or this instance was '
   375                 'never told about this request: %r' % frame
   375                 b'never told about this request: %r' % frame
   376             )
   376             )
   377 
   377 
   378         response = self._responses[frame.requestid]
   378         response = self._responses[frame.requestid]
   379 
   379 
   380         if action == 'responsedata':
   380         if action == b'responsedata':
   381             # Any failures processing this frame should bubble up to the
   381             # Any failures processing this frame should bubble up to the
   382             # future tracking the request.
   382             # future tracking the request.
   383             try:
   383             try:
   384                 self._processresponsedata(frame, meta, response)
   384                 self._processresponsedata(frame, meta, response)
   385             except BaseException as e:
   385             except BaseException as e:
   395                     response._oninputcomplete()
   395                     response._oninputcomplete()
   396                 else:
   396                 else:
   397                     response._onerror(e)
   397                     response._onerror(e)
   398         else:
   398         else:
   399             raise error.ProgrammingError(
   399             raise error.ProgrammingError(
   400                 'unhandled action from clientreactor: %s' % action
   400                 b'unhandled action from clientreactor: %s' % action
   401             )
   401             )
   402 
   402 
   403     def _processresponsedata(self, frame, meta, response):
   403     def _processresponsedata(self, frame, meta, response):
   404         # This can raise. The caller can handle it.
   404         # This can raise. The caller can handle it.
   405         response._onresponsedata(meta['data'])
   405         response._onresponsedata(meta[b'data'])
   406 
   406 
   407         # We need to be careful about resolving futures prematurely. If a
   407         # We need to be careful about resolving futures prematurely. If a
   408         # response is a redirect response, resolving the future before the
   408         # response is a redirect response, resolving the future before the
   409         # redirect is processed would result in the consumer seeing an
   409         # redirect is processed would result in the consumer seeing an
   410         # empty stream of objects, since they'd be consuming our
   410         # empty stream of objects, since they'd be consuming our
   412         #
   412         #
   413         # Our strategy is to not resolve/finish the request until either
   413         # Our strategy is to not resolve/finish the request until either
   414         # EOS occurs or until the initial response object is fully received.
   414         # EOS occurs or until the initial response object is fully received.
   415 
   415 
   416         # Always react to eos.
   416         # Always react to eos.
   417         if meta['eos']:
   417         if meta[b'eos']:
   418             response._oninputcomplete()
   418             response._oninputcomplete()
   419             del self._requests[frame.requestid]
   419             del self._requests[frame.requestid]
   420 
   420 
   421         # Not EOS but we haven't decoded the initial response object yet.
   421         # Not EOS but we haven't decoded the initial response object yet.
   422         # Return and wait for more data.
   422         # Return and wait for more data.
   444             self._futures[frame.requestid].set_result(decoded)
   444             self._futures[frame.requestid].set_result(decoded)
   445             del self._futures[frame.requestid]
   445             del self._futures[frame.requestid]
   446 
   446 
   447     def _followredirect(self, requestid, redirect):
   447     def _followredirect(self, requestid, redirect):
   448         """Called to initiate redirect following for a request."""
   448         """Called to initiate redirect following for a request."""
   449         self._ui.note(_('(following redirect to %s)\n') % redirect.url)
   449         self._ui.note(_(b'(following redirect to %s)\n') % redirect.url)
   450 
   450 
   451         # TODO handle framed responses.
   451         # TODO handle framed responses.
   452         if redirect.mediatype != b'application/mercurial-cbor':
   452         if redirect.mediatype != b'application/mercurial-cbor':
   453             raise error.Abort(
   453             raise error.Abort(
   454                 _('cannot handle redirects for the %s media type')
   454                 _(b'cannot handle redirects for the %s media type')
   455                 % redirect.mediatype
   455                 % redirect.mediatype
   456             )
   456             )
   457 
   457 
   458         if redirect.fullhashes:
   458         if redirect.fullhashes:
   459             self._ui.warn(
   459             self._ui.warn(
   460                 _(
   460                 _(
   461                     '(support for validating hashes on content '
   461                     b'(support for validating hashes on content '
   462                     'redirects not supported)\n'
   462                     b'redirects not supported)\n'
   463                 )
   463                 )
   464             )
   464             )
   465 
   465 
   466         if redirect.serverdercerts or redirect.servercadercerts:
   466         if redirect.serverdercerts or redirect.servercadercerts:
   467             self._ui.warn(
   467             self._ui.warn(
   468                 _(
   468                 _(
   469                     '(support for pinning server certificates on '
   469                     b'(support for pinning server certificates on '
   470                     'content redirects not supported)\n'
   470                     b'content redirects not supported)\n'
   471                 )
   471                 )
   472             )
   472             )
   473 
   473 
   474         headers = {
   474         headers = {
   475             r'Accept': redirect.mediatype,
   475             r'Accept': redirect.mediatype,
   479 
   479 
   480         try:
   480         try:
   481             res = self._opener.open(req)
   481             res = self._opener.open(req)
   482         except util.urlerr.httperror as e:
   482         except util.urlerr.httperror as e:
   483             if e.code == 401:
   483             if e.code == 401:
   484                 raise error.Abort(_('authorization failed'))
   484                 raise error.Abort(_(b'authorization failed'))
   485             raise
   485             raise
   486         except util.httplib.HTTPException as e:
   486         except util.httplib.HTTPException as e:
   487             self._ui.debug('http error requesting %s\n' % req.get_full_url())
   487             self._ui.debug(b'http error requesting %s\n' % req.get_full_url())
   488             self._ui.traceback()
   488             self._ui.traceback()
   489             raise IOError(None, e)
   489             raise IOError(None, e)
   490 
   490 
   491         urlmod.wrapresponse(res)
   491         urlmod.wrapresponse(res)
   492 
   492 
   565 def decodepushkey(objs):
   565 def decodepushkey(objs):
   566     return next(objs)
   566     return next(objs)
   567 
   567 
   568 
   568 
   569 COMMAND_DECODERS = {
   569 COMMAND_DECODERS = {
   570     'branchmap': decodebranchmap,
   570     b'branchmap': decodebranchmap,
   571     'heads': decodeheads,
   571     b'heads': decodeheads,
   572     'known': decodeknown,
   572     b'known': decodeknown,
   573     'listkeys': decodelistkeys,
   573     b'listkeys': decodelistkeys,
   574     'lookup': decodelookup,
   574     b'lookup': decodelookup,
   575     'pushkey': decodepushkey,
   575     b'pushkey': decodepushkey,
   576 }
   576 }