equal
deleted
inserted
replaced
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 |