mercurial/wireprotoframing.py
branchstable
changeset 49366 288de6f5d724
parent 48946 642e31cb55f0
child 50929 18c8c18993f0
equal deleted inserted replaced
49364:e8ea403b1c46 49366:288de6f5d724
     7 
     7 
     8 # This file contains functionality to support the unified frame-based wire
     8 # This file contains functionality to support the unified frame-based wire
     9 # protocol. For details about the protocol, see
     9 # protocol. For details about the protocol, see
    10 # `hg help internals.wireprotocol`.
    10 # `hg help internals.wireprotocol`.
    11 
    11 
    12 from __future__ import absolute_import
       
    13 
    12 
    14 import collections
    13 import collections
    15 import struct
    14 import struct
    16 
    15 
    17 from .i18n import _
    16 from .i18n import _
   121 ARGUMENT_RECORD_HEADER = struct.Struct('<HH')
   120 ARGUMENT_RECORD_HEADER = struct.Struct('<HH')
   122 
   121 
   123 
   122 
   124 def humanflags(mapping, value):
   123 def humanflags(mapping, value):
   125     """Convert a numeric flags value to a human value, using a mapping table."""
   124     """Convert a numeric flags value to a human value, using a mapping table."""
   126     namemap = {v: k for k, v in pycompat.iteritems(mapping)}
   125     namemap = {v: k for k, v in mapping.items()}
   127     flags = []
   126     flags = []
   128     val = 1
   127     val = 1
   129     while value >= val:
   128     while value >= val:
   130         if value & val:
   129         if value & val:
   131             flags.append(namemap.get(val, b'<unknown 0x%02x>' % val))
   130             flags.append(namemap.get(val, b'<unknown 0x%02x>' % val))
   133 
   132 
   134     return b'|'.join(flags)
   133     return b'|'.join(flags)
   135 
   134 
   136 
   135 
   137 @attr.s(slots=True)
   136 @attr.s(slots=True)
   138 class frameheader(object):
   137 class frameheader:
   139     """Represents the data in a frame header."""
   138     """Represents the data in a frame header."""
   140 
   139 
   141     length = attr.ib()
   140     length = attr.ib()
   142     requestid = attr.ib()
   141     requestid = attr.ib()
   143     streamid = attr.ib()
   142     streamid = attr.ib()
   145     typeid = attr.ib()
   144     typeid = attr.ib()
   146     flags = attr.ib()
   145     flags = attr.ib()
   147 
   146 
   148 
   147 
   149 @attr.s(slots=True, repr=False)
   148 @attr.s(slots=True, repr=False)
   150 class frame(object):
   149 class frame:
   151     """Represents a parsed frame."""
   150     """Represents a parsed frame."""
   152 
   151 
   153     requestid = attr.ib()
   152     requestid = attr.ib()
   154     streamid = attr.ib()
   153     streamid = attr.ib()
   155     streamflags = attr.ib()
   154     streamflags = attr.ib()
   158     payload = attr.ib()
   157     payload = attr.ib()
   159 
   158 
   160     @encoding.strmethod
   159     @encoding.strmethod
   161     def __repr__(self):
   160     def __repr__(self):
   162         typename = b'<unknown 0x%02x>' % self.typeid
   161         typename = b'<unknown 0x%02x>' % self.typeid
   163         for name, value in pycompat.iteritems(FRAME_TYPES):
   162         for name, value in FRAME_TYPES.items():
   164             if value == self.typeid:
   163             if value == self.typeid:
   165                 typename = name
   164                 typename = name
   166                 break
   165                 break
   167 
   166 
   168         return (
   167         return (
   588         flags=0,
   587         flags=0,
   589         payload=payload,
   588         payload=payload,
   590     )
   589     )
   591 
   590 
   592 
   591 
   593 class bufferingcommandresponseemitter(object):
   592 class bufferingcommandresponseemitter:
   594     """Helper object to emit command response frames intelligently.
   593     """Helper object to emit command response frames intelligently.
   595 
   594 
   596     Raw command response data is likely emitted in chunks much smaller
   595     Raw command response data is likely emitted in chunks much smaller
   597     than what can fit in a single frame. This class exists to buffer
   596     than what can fit in a single frame. This class exists to buffer
   598     chunks until enough data is available to fit in a single frame.
   597     chunks until enough data is available to fit in a single frame.
   698 
   697 
   699 # TODO consider defining encoders/decoders using the util.compressionengine
   698 # TODO consider defining encoders/decoders using the util.compressionengine
   700 # mechanism.
   699 # mechanism.
   701 
   700 
   702 
   701 
   703 class identityencoder(object):
   702 class identityencoder:
   704     """Encoder for the "identity" stream encoding profile."""
   703     """Encoder for the "identity" stream encoding profile."""
   705 
   704 
   706     def __init__(self, ui):
   705     def __init__(self, ui):
   707         pass
   706         pass
   708 
   707 
   714 
   713 
   715     def finish(self):
   714     def finish(self):
   716         return b''
   715         return b''
   717 
   716 
   718 
   717 
   719 class identitydecoder(object):
   718 class identitydecoder:
   720     """Decoder for the "identity" stream encoding profile."""
   719     """Decoder for the "identity" stream encoding profile."""
   721 
   720 
   722     def __init__(self, ui, extraobjs):
   721     def __init__(self, ui, extraobjs):
   723         if extraobjs:
   722         if extraobjs:
   724             raise error.Abort(
   723             raise error.Abort(
   727 
   726 
   728     def decode(self, data):
   727     def decode(self, data):
   729         return data
   728         return data
   730 
   729 
   731 
   730 
   732 class zlibencoder(object):
   731 class zlibencoder:
   733     def __init__(self, ui):
   732     def __init__(self, ui):
   734         import zlib
   733         import zlib
   735 
   734 
   736         self._zlib = zlib
   735         self._zlib = zlib
   737         self._compressor = zlib.compressobj()
   736         self._compressor = zlib.compressobj()
   748         res = self._compressor.flush(self._zlib.Z_FINISH)
   747         res = self._compressor.flush(self._zlib.Z_FINISH)
   749         self._compressor = None
   748         self._compressor = None
   750         return res
   749         return res
   751 
   750 
   752 
   751 
   753 class zlibdecoder(object):
   752 class zlibdecoder:
   754     def __init__(self, ui, extraobjs):
   753     def __init__(self, ui, extraobjs):
   755         import zlib
   754         import zlib
   756 
   755 
   757         if extraobjs:
   756         if extraobjs:
   758             raise error.Abort(
   757             raise error.Abort(
   760             )
   759             )
   761 
   760 
   762         self._decompressor = zlib.decompressobj()
   761         self._decompressor = zlib.decompressobj()
   763 
   762 
   764     def decode(self, data):
   763     def decode(self, data):
   765         # Python 2's zlib module doesn't use the buffer protocol and can't
       
   766         # handle all bytes-like types.
       
   767         if not pycompat.ispy3 and isinstance(data, bytearray):
       
   768             data = bytes(data)
       
   769 
       
   770         return self._decompressor.decompress(data)
   764         return self._decompressor.decompress(data)
   771 
   765 
   772 
   766 
   773 class zstdbaseencoder(object):
   767 class zstdbaseencoder:
   774     def __init__(self, level):
   768     def __init__(self, level):
   775         from . import zstd
   769         from . import zstd
   776 
   770 
   777         self._zstd = zstd
   771         self._zstd = zstd
   778         cctx = zstd.ZstdCompressor(level=level)
   772         cctx = zstd.ZstdCompressor(level=level)
   796 class zstd8mbencoder(zstdbaseencoder):
   790 class zstd8mbencoder(zstdbaseencoder):
   797     def __init__(self, ui):
   791     def __init__(self, ui):
   798         super(zstd8mbencoder, self).__init__(3)
   792         super(zstd8mbencoder, self).__init__(3)
   799 
   793 
   800 
   794 
   801 class zstdbasedecoder(object):
   795 class zstdbasedecoder:
   802     def __init__(self, maxwindowsize):
   796     def __init__(self, maxwindowsize):
   803         from . import zstd
   797         from . import zstd
   804 
   798 
   805         dctx = zstd.ZstdDecompressor(max_window_size=maxwindowsize)
   799         dctx = zstd.ZstdDecompressor(max_window_size=maxwindowsize)
   806         self._decompressor = dctx.decompressobj()
   800         self._decompressor = dctx.decompressobj()
   846 
   840 
   847     STREAM_ENCODERS[b'identity'] = (identityencoder, identitydecoder)
   841     STREAM_ENCODERS[b'identity'] = (identityencoder, identitydecoder)
   848     STREAM_ENCODERS_ORDER.append(b'identity')
   842     STREAM_ENCODERS_ORDER.append(b'identity')
   849 
   843 
   850 
   844 
   851 class stream(object):
   845 class stream:
   852     """Represents a logical unidirectional series of frames."""
   846     """Represents a logical unidirectional series of frames."""
   853 
   847 
   854     def __init__(self, streamid, active=False):
   848     def __init__(self, streamid, active=False):
   855         self.streamid = streamid
   849         self.streamid = streamid
   856         self._active = active
   850         self._active = active
   999 DEFAULT_PROTOCOL_SETTINGS = {
   993 DEFAULT_PROTOCOL_SETTINGS = {
  1000     b'contentencodings': [b'identity'],
   994     b'contentencodings': [b'identity'],
  1001 }
   995 }
  1002 
   996 
  1003 
   997 
  1004 class serverreactor(object):
   998 class serverreactor:
  1005     """Holds state of a server handling frame-based protocol requests.
   999     """Holds state of a server handling frame-based protocol requests.
  1006 
  1000 
  1007     This class is the "brain" of the unified frame-based protocol server
  1001     This class is the "brain" of the unified frame-based protocol server
  1008     component. While the protocol is stateless from the perspective of
  1002     component. While the protocol is stateless from the perspective of
  1009     requests/commands, something needs to track which frames have been
  1003     requests/commands, something needs to track which frames have been
  1687 
  1681 
  1688     def _onframeerrored(self, frame):
  1682     def _onframeerrored(self, frame):
  1689         return self._makeerrorresult(_(b'server already errored'))
  1683         return self._makeerrorresult(_(b'server already errored'))
  1690 
  1684 
  1691 
  1685 
  1692 class commandrequest(object):
  1686 class commandrequest:
  1693     """Represents a request to run a command."""
  1687     """Represents a request to run a command."""
  1694 
  1688 
  1695     def __init__(self, requestid, name, args, datafh=None, redirect=None):
  1689     def __init__(self, requestid, name, args, datafh=None, redirect=None):
  1696         self.requestid = requestid
  1690         self.requestid = requestid
  1697         self.name = name
  1691         self.name = name
  1699         self.datafh = datafh
  1693         self.datafh = datafh
  1700         self.redirect = redirect
  1694         self.redirect = redirect
  1701         self.state = b'pending'
  1695         self.state = b'pending'
  1702 
  1696 
  1703 
  1697 
  1704 class clientreactor(object):
  1698 class clientreactor:
  1705     """Holds state of a client issuing frame-based protocol requests.
  1699     """Holds state of a client issuing frame-based protocol requests.
  1706 
  1700 
  1707     This is like ``serverreactor`` but for client-side state.
  1701     This is like ``serverreactor`` but for client-side state.
  1708 
  1702 
  1709     Each instance is bound to the lifetime of a connection. For persistent
  1703     Each instance is bound to the lifetime of a connection. For persistent