##// END OF EJS Templates
wireprotov2: handle sender protocol settings frames...
Gregory Szorc -
r40162:327d40b9 default
parent child Browse files
Show More
@@ -674,6 +674,10 b' def ensureserverstream(stream):'
674 'numbered streams; %d is not even' %
674 'numbered streams; %d is not even' %
675 stream.streamid)
675 stream.streamid)
676
676
677 DEFAULT_PROTOCOL_SETTINGS = {
678 'contentencodings': [b'identity'],
679 }
680
677 class serverreactor(object):
681 class serverreactor(object):
678 """Holds state of a server handling frame-based protocol requests.
682 """Holds state of a server handling frame-based protocol requests.
679
683
@@ -750,7 +754,7 b' class serverreactor(object):'
750 sender cannot receive until all data has been transmitted.
754 sender cannot receive until all data has been transmitted.
751 """
755 """
752 self._deferoutput = deferoutput
756 self._deferoutput = deferoutput
753 self._state = 'idle'
757 self._state = 'initial'
754 self._nextoutgoingstreamid = 2
758 self._nextoutgoingstreamid = 2
755 self._bufferedframegens = []
759 self._bufferedframegens = []
756 # stream id -> stream instance for all active streams from the client.
760 # stream id -> stream instance for all active streams from the client.
@@ -763,6 +767,11 b' class serverreactor(object):'
763 # set.
767 # set.
764 self._activecommands = set()
768 self._activecommands = set()
765
769
770 self._protocolsettingsdecoder = None
771
772 # Sender protocol settings are optional. Set implied default values.
773 self._sendersettings = dict(DEFAULT_PROTOCOL_SETTINGS)
774
766 def onframerecv(self, frame):
775 def onframerecv(self, frame):
767 """Process a frame that has been received off the wire.
776 """Process a frame that has been received off the wire.
768
777
@@ -794,6 +803,8 b' class serverreactor(object):'
794 del self._incomingstreams[frame.streamid]
803 del self._incomingstreams[frame.streamid]
795
804
796 handlers = {
805 handlers = {
806 'initial': self._onframeinitial,
807 'protocol-settings-receiving': self._onframeprotocolsettings,
797 'idle': self._onframeidle,
808 'idle': self._onframeidle,
798 'command-receiving': self._onframecommandreceiving,
809 'command-receiving': self._onframecommandreceiving,
799 'errored': self._onframeerrored,
810 'errored': self._onframeerrored,
@@ -1062,6 +1073,85 b' class serverreactor(object):'
1062 _('received command request frame with neither new nor '
1073 _('received command request frame with neither new nor '
1063 'continuation flags set'))
1074 'continuation flags set'))
1064
1075
1076 def _onframeinitial(self, frame):
1077 # Called when we receive a frame when in the "initial" state.
1078 if frame.typeid == FRAME_TYPE_SENDER_PROTOCOL_SETTINGS:
1079 self._state = 'protocol-settings-receiving'
1080 self._protocolsettingsdecoder = cborutil.bufferingdecoder()
1081 return self._onframeprotocolsettings(frame)
1082
1083 elif frame.typeid == FRAME_TYPE_COMMAND_REQUEST:
1084 self._state = 'idle'
1085 return self._onframeidle(frame)
1086
1087 else:
1088 self._state = 'errored'
1089 return self._makeerrorresult(
1090 _('expected sender protocol settings or command request '
1091 'frame; got %d') % frame.typeid)
1092
1093 def _onframeprotocolsettings(self, frame):
1094 assert self._state == 'protocol-settings-receiving'
1095 assert self._protocolsettingsdecoder is not None
1096
1097 if frame.typeid != FRAME_TYPE_SENDER_PROTOCOL_SETTINGS:
1098 self._state = 'errored'
1099 return self._makeerrorresult(
1100 _('expected sender protocol settings frame; got %d') %
1101 frame.typeid)
1102
1103 more = frame.flags & FLAG_SENDER_PROTOCOL_SETTINGS_CONTINUATION
1104 eos = frame.flags & FLAG_SENDER_PROTOCOL_SETTINGS_EOS
1105
1106 if more and eos:
1107 self._state = 'errored'
1108 return self._makeerrorresult(
1109 _('sender protocol settings frame cannot have both '
1110 'continuation and end of stream flags set'))
1111
1112 if not more and not eos:
1113 self._state = 'errored'
1114 return self._makeerrorresult(
1115 _('sender protocol settings frame must have continuation or '
1116 'end of stream flag set'))
1117
1118 # TODO establish limits for maximum amount of data that can be
1119 # buffered.
1120 try:
1121 self._protocolsettingsdecoder.decode(frame.payload)
1122 except Exception as e:
1123 self._state = 'errored'
1124 return self._makeerrorresult(
1125 _('error decoding CBOR from sender protocol settings frame: %s')
1126 % stringutil.forcebytestr(e))
1127
1128 if more:
1129 return self._makewantframeresult()
1130
1131 assert eos
1132
1133 decoded = self._protocolsettingsdecoder.getavailable()
1134 self._protocolsettingsdecoder = None
1135
1136 if not decoded:
1137 self._state = 'errored'
1138 return self._makeerrorresult(
1139 _('sender protocol settings frame did not contain CBOR data'))
1140 elif len(decoded) > 1:
1141 self._state = 'errored'
1142 return self._makeerrorresult(
1143 _('sender protocol settings frame contained multiple CBOR '
1144 'values'))
1145
1146 d = decoded[0]
1147
1148 if b'contentencodings' in d:
1149 self._sendersettings['contentencodings'] = d[b'contentencodings']
1150
1151 self._state = 'idle'
1152
1153 return self._makewantframeresult()
1154
1065 def _onframeidle(self, frame):
1155 def _onframeidle(self, frame):
1066 # The only frame type that should be received in this state is a
1156 # The only frame type that should be received in this state is a
1067 # command request.
1157 # command request.
@@ -9,6 +9,9 b' from mercurial import ('
9 util,
9 util,
10 wireprotoframing as framing,
10 wireprotoframing as framing,
11 )
11 )
12 from mercurial.utils import (
13 cborutil,
14 )
12
15
13 ffs = framing.makeframefromhumanstring
16 ffs = framing.makeframefromhumanstring
14
17
@@ -193,7 +196,8 b' class ServerReactorTests(unittest.TestCa'
193 ffs(b'1 1 stream-begin command-data 0 ignored'))
196 ffs(b'1 1 stream-begin command-data 0 ignored'))
194 self.assertaction(result, b'error')
197 self.assertaction(result, b'error')
195 self.assertEqual(result[1], {
198 self.assertEqual(result[1], {
196 b'message': b'expected command request frame; got 2',
199 b'message': b'expected sender protocol settings or command request '
200 b'frame; got 2',
197 })
201 })
198
202
199 def testunexpectedcommanddatareceiving(self):
203 def testunexpectedcommanddatareceiving(self):
@@ -494,6 +498,105 b' class ServerReactorTests(unittest.TestCa'
494 results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
498 results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
495 self.assertaction(results[0], b'runcommand')
499 self.assertaction(results[0], b'runcommand')
496
500
501 def testprotocolsettingsnoflags(self):
502 result = self._sendsingleframe(
503 makereactor(),
504 ffs(b'0 1 stream-begin sender-protocol-settings 0 '))
505 self.assertaction(result, b'error')
506 self.assertEqual(result[1], {
507 b'message': b'sender protocol settings frame must have '
508 b'continuation or end of stream flag set',
509 })
510
511 def testprotocolsettingsconflictflags(self):
512 result = self._sendsingleframe(
513 makereactor(),
514 ffs(b'0 1 stream-begin sender-protocol-settings continuation|eos '))
515 self.assertaction(result, b'error')
516 self.assertEqual(result[1], {
517 b'message': b'sender protocol settings frame cannot have both '
518 b'continuation and end of stream flags set',
519 })
520
521 def testprotocolsettingsemptypayload(self):
522 result = self._sendsingleframe(
523 makereactor(),
524 ffs(b'0 1 stream-begin sender-protocol-settings eos '))
525 self.assertaction(result, b'error')
526 self.assertEqual(result[1], {
527 b'message': b'sender protocol settings frame did not contain CBOR '
528 b'data',
529 })
530
531 def testprotocolsettingsmultipleobjects(self):
532 result = self._sendsingleframe(
533 makereactor(),
534 ffs(b'0 1 stream-begin sender-protocol-settings eos '
535 b'\x46foobar\x43foo'))
536 self.assertaction(result, b'error')
537 self.assertEqual(result[1], {
538 b'message': b'sender protocol settings frame contained multiple '
539 b'CBOR values',
540 })
541
542 def testprotocolsettingscontentencodings(self):
543 reactor = makereactor()
544
545 result = self._sendsingleframe(
546 reactor,
547 ffs(b'0 1 stream-begin sender-protocol-settings eos '
548 b'cbor:{b"contentencodings": [b"a", b"b"]}'))
549 self.assertaction(result, b'wantframe')
550
551 self.assertEqual(reactor._state, b'idle')
552 self.assertEqual(reactor._sendersettings[b'contentencodings'],
553 [b'a', b'b'])
554
555 def testprotocolsettingsmultipleframes(self):
556 reactor = makereactor()
557
558 data = b''.join(cborutil.streamencode({
559 b'contentencodings': [b'value1', b'value2'],
560 }))
561
562 results = list(sendframes(reactor, [
563 ffs(b'0 1 stream-begin sender-protocol-settings continuation %s' %
564 data[0:5]),
565 ffs(b'0 1 0 sender-protocol-settings eos %s' % data[5:]),
566 ]))
567
568 self.assertEqual(len(results), 2)
569
570 self.assertaction(results[0], b'wantframe')
571 self.assertaction(results[1], b'wantframe')
572
573 self.assertEqual(reactor._state, b'idle')
574 self.assertEqual(reactor._sendersettings[b'contentencodings'],
575 [b'value1', b'value2'])
576
577 def testprotocolsettingsbadcbor(self):
578 result = self._sendsingleframe(
579 makereactor(),
580 ffs(b'0 1 stream-begin sender-protocol-settings eos badvalue'))
581 self.assertaction(result, b'error')
582
583 def testprotocolsettingsnoninitial(self):
584 # Cannot have protocol settings frames as non-initial frames.
585 reactor = makereactor()
586
587 stream = framing.stream(1)
588 results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
589 self.assertEqual(len(results), 1)
590 self.assertaction(results[0], b'runcommand')
591
592 result = self._sendsingleframe(
593 reactor,
594 ffs(b'0 1 0 sender-protocol-settings eos '))
595 self.assertaction(result, b'error')
596 self.assertEqual(result[1], {
597 b'message': b'expected command request frame; got 8',
598 })
599
497 if __name__ == '__main__':
600 if __name__ == '__main__':
498 import silenttestrunner
601 import silenttestrunner
499 silenttestrunner.main(__name__)
602 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now