diff -r e2fe1074024c -r 327d40b94bed mercurial/wireprotoframing.py --- a/mercurial/wireprotoframing.py Thu Oct 04 14:05:16 2018 -0700 +++ b/mercurial/wireprotoframing.py Thu Oct 04 16:26:45 2018 -0700 @@ -674,6 +674,10 @@ 'numbered streams; %d is not even' % stream.streamid) +DEFAULT_PROTOCOL_SETTINGS = { + 'contentencodings': [b'identity'], +} + class serverreactor(object): """Holds state of a server handling frame-based protocol requests. @@ -750,7 +754,7 @@ sender cannot receive until all data has been transmitted. """ self._deferoutput = deferoutput - self._state = 'idle' + self._state = 'initial' self._nextoutgoingstreamid = 2 self._bufferedframegens = [] # stream id -> stream instance for all active streams from the client. @@ -763,6 +767,11 @@ # set. self._activecommands = set() + self._protocolsettingsdecoder = None + + # Sender protocol settings are optional. Set implied default values. + self._sendersettings = dict(DEFAULT_PROTOCOL_SETTINGS) + def onframerecv(self, frame): """Process a frame that has been received off the wire. @@ -794,6 +803,8 @@ del self._incomingstreams[frame.streamid] handlers = { + 'initial': self._onframeinitial, + 'protocol-settings-receiving': self._onframeprotocolsettings, 'idle': self._onframeidle, 'command-receiving': self._onframecommandreceiving, 'errored': self._onframeerrored, @@ -1062,6 +1073,85 @@ _('received command request frame with neither new nor ' 'continuation flags set')) + def _onframeinitial(self, frame): + # Called when we receive a frame when in the "initial" state. + if frame.typeid == FRAME_TYPE_SENDER_PROTOCOL_SETTINGS: + self._state = 'protocol-settings-receiving' + self._protocolsettingsdecoder = cborutil.bufferingdecoder() + return self._onframeprotocolsettings(frame) + + elif frame.typeid == FRAME_TYPE_COMMAND_REQUEST: + self._state = 'idle' + return self._onframeidle(frame) + + else: + self._state = 'errored' + return self._makeerrorresult( + _('expected sender protocol settings or command request ' + 'frame; got %d') % frame.typeid) + + def _onframeprotocolsettings(self, frame): + assert self._state == 'protocol-settings-receiving' + assert self._protocolsettingsdecoder is not None + + if frame.typeid != FRAME_TYPE_SENDER_PROTOCOL_SETTINGS: + self._state = 'errored' + return self._makeerrorresult( + _('expected sender protocol settings frame; got %d') % + frame.typeid) + + more = frame.flags & FLAG_SENDER_PROTOCOL_SETTINGS_CONTINUATION + eos = frame.flags & FLAG_SENDER_PROTOCOL_SETTINGS_EOS + + if more and eos: + self._state = 'errored' + return self._makeerrorresult( + _('sender protocol settings frame cannot have both ' + 'continuation and end of stream flags set')) + + if not more and not eos: + self._state = 'errored' + return self._makeerrorresult( + _('sender protocol settings frame must have continuation or ' + 'end of stream flag set')) + + # TODO establish limits for maximum amount of data that can be + # buffered. + try: + self._protocolsettingsdecoder.decode(frame.payload) + except Exception as e: + self._state = 'errored' + return self._makeerrorresult( + _('error decoding CBOR from sender protocol settings frame: %s') + % stringutil.forcebytestr(e)) + + if more: + return self._makewantframeresult() + + assert eos + + decoded = self._protocolsettingsdecoder.getavailable() + self._protocolsettingsdecoder = None + + if not decoded: + self._state = 'errored' + return self._makeerrorresult( + _('sender protocol settings frame did not contain CBOR data')) + elif len(decoded) > 1: + self._state = 'errored' + return self._makeerrorresult( + _('sender protocol settings frame contained multiple CBOR ' + 'values')) + + d = decoded[0] + + if b'contentencodings' in d: + self._sendersettings['contentencodings'] = d[b'contentencodings'] + + self._state = 'idle' + + return self._makewantframeresult() + def _onframeidle(self, frame): # The only frame type that should be received in this state is a # command request.