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 = 'i |
|
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 |
|
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