Show More
@@ -674,6 +674,10 b' def ensureserverstream(stream):' | |||
|
674 | 674 | 'numbered streams; %d is not even' % |
|
675 | 675 | stream.streamid) |
|
676 | 676 | |
|
677 | DEFAULT_PROTOCOL_SETTINGS = { | |
|
678 | 'contentencodings': [b'identity'], | |
|
679 | } | |
|
680 | ||
|
677 | 681 | class serverreactor(object): |
|
678 | 682 | """Holds state of a server handling frame-based protocol requests. |
|
679 | 683 | |
@@ -750,7 +754,7 b' class serverreactor(object):' | |||
|
750 | 754 | sender cannot receive until all data has been transmitted. |
|
751 | 755 | """ |
|
752 | 756 | self._deferoutput = deferoutput |
|
753 |
self._state = 'i |
|
|
757 | self._state = 'initial' | |
|
754 | 758 | self._nextoutgoingstreamid = 2 |
|
755 | 759 | self._bufferedframegens = [] |
|
756 | 760 | # stream id -> stream instance for all active streams from the client. |
@@ -763,6 +767,11 b' class serverreactor(object):' | |||
|
763 | 767 | # set. |
|
764 | 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 | 775 | def onframerecv(self, frame): |
|
767 | 776 | """Process a frame that has been received off the wire. |
|
768 | 777 | |
@@ -794,6 +803,8 b' class serverreactor(object):' | |||
|
794 | 803 | del self._incomingstreams[frame.streamid] |
|
795 | 804 | |
|
796 | 805 | handlers = { |
|
806 | 'initial': self._onframeinitial, | |
|
807 | 'protocol-settings-receiving': self._onframeprotocolsettings, | |
|
797 | 808 | 'idle': self._onframeidle, |
|
798 | 809 | 'command-receiving': self._onframecommandreceiving, |
|
799 | 810 | 'errored': self._onframeerrored, |
@@ -1062,6 +1073,85 b' class serverreactor(object):' | |||
|
1062 | 1073 | _('received command request frame with neither new nor ' |
|
1063 | 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 | 1155 | def _onframeidle(self, frame): |
|
1066 | 1156 | # The only frame type that should be received in this state is a |
|
1067 | 1157 | # command request. |
@@ -9,6 +9,9 b' from mercurial import (' | |||
|
9 | 9 | util, |
|
10 | 10 | wireprotoframing as framing, |
|
11 | 11 | ) |
|
12 | from mercurial.utils import ( | |
|
13 | cborutil, | |
|
14 | ) | |
|
12 | 15 | |
|
13 | 16 | ffs = framing.makeframefromhumanstring |
|
14 | 17 | |
@@ -193,7 +196,8 b' class ServerReactorTests(unittest.TestCa' | |||
|
193 | 196 | ffs(b'1 1 stream-begin command-data 0 ignored')) |
|
194 | 197 | self.assertaction(result, b'error') |
|
195 | 198 | self.assertEqual(result[1], { |
|
196 |
b'message': b'expected command request |
|
|
199 | b'message': b'expected sender protocol settings or command request ' | |
|
200 | b'frame; got 2', | |
|
197 | 201 | }) |
|
198 | 202 | |
|
199 | 203 | def testunexpectedcommanddatareceiving(self): |
@@ -494,6 +498,105 b' class ServerReactorTests(unittest.TestCa' | |||
|
494 | 498 | results = list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
|
495 | 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 | 600 | if __name__ == '__main__': |
|
498 | 601 | import silenttestrunner |
|
499 | 602 | silenttestrunner.main(__name__) |
General Comments 0
You need to be logged in to leave comments.
Login now