From patchwork Thu Mar 22 01:41:44 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: D2872: wireproto: define human output side channel frame From: phabricator X-Patchwork-Id: 29761 Message-Id: To: mercurial-devel@mercurial-scm.org Date: Thu, 22 Mar 2018 01:41:44 +0000 This revision was automatically updated to reflect the committed changes. Closed by commit rHG0a6c5cc09a88: wireproto: define human output side channel frame (authored by indygreg, committed by ). REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D2872?vs=7148&id=7236 REVISION DETAIL https://phab.mercurial-scm.org/D2872 AFFECTED FILES mercurial/help/internals/wireprotocol.txt mercurial/wireprotoframing.py tests/test-wireproto-serverreactor.py CHANGE DETAILS To: indygreg, #hg-reviewers, durin42 Cc: mercurial-devel diff --git a/tests/test-wireproto-serverreactor.py b/tests/test-wireproto-serverreactor.py --- a/tests/test-wireproto-serverreactor.py +++ b/tests/test-wireproto-serverreactor.py @@ -67,6 +67,109 @@ ffs(b'1 command-data eos %s' % data.getvalue()), ]) + def testtextoutputexcessiveargs(self): + """At most 255 formatting arguments are allowed.""" + with self.assertRaisesRegexp(ValueError, + 'cannot use more than 255 formatting'): + args = [b'x' for i in range(256)] + list(framing.createtextoutputframe(1, [(b'bleh', args, [])])) + + def testtextoutputexcessivelabels(self): + """At most 255 labels are allowed.""" + with self.assertRaisesRegexp(ValueError, + 'cannot use more than 255 labels'): + labels = [b'l' for i in range(256)] + list(framing.createtextoutputframe(1, [(b'bleh', [], labels)])) + + def testtextoutputformattingstringtype(self): + """Formatting string must be bytes.""" + with self.assertRaisesRegexp(ValueError, 'must use bytes formatting '): + list(framing.createtextoutputframe(1, [ + (b'foo'.decode('ascii'), [], [])])) + + def testtextoutputargumentbytes(self): + with self.assertRaisesRegexp(ValueError, 'must use bytes for argument'): + list(framing.createtextoutputframe(1, [ + (b'foo', [b'foo'.decode('ascii')], [])])) + + def testtextoutputlabelbytes(self): + with self.assertRaisesRegexp(ValueError, 'must use bytes for labels'): + list(framing.createtextoutputframe(1, [ + (b'foo', [], [b'foo'.decode('ascii')])])) + + def testtextoutputtoolongformatstring(self): + with self.assertRaisesRegexp(ValueError, + 'formatting string cannot be longer than'): + list(framing.createtextoutputframe(1, [ + (b'x' * 65536, [], [])])) + + def testtextoutputtoolongargumentstring(self): + with self.assertRaisesRegexp(ValueError, + 'argument string cannot be longer than'): + list(framing.createtextoutputframe(1, [ + (b'bleh', [b'x' * 65536], [])])) + + def testtextoutputtoolonglabelstring(self): + with self.assertRaisesRegexp(ValueError, + 'label string cannot be longer than'): + list(framing.createtextoutputframe(1, [ + (b'bleh', [], [b'x' * 65536])])) + + def testtextoutput1simpleatom(self): + val = list(framing.createtextoutputframe(1, [ + (b'foo', [], [])])) + + self.assertEqual(val, [ + ffs(br'1 text-output 0 \x03\x00\x00\x00foo'), + ]) + + def testtextoutput2simpleatoms(self): + val = list(framing.createtextoutputframe(1, [ + (b'foo', [], []), + (b'bar', [], []), + ])) + + self.assertEqual(val, [ + ffs(br'1 text-output 0 \x03\x00\x00\x00foo\x03\x00\x00\x00bar'), + ]) + + def testtextoutput1arg(self): + val = list(framing.createtextoutputframe(1, [ + (b'foo %s', [b'val1'], []), + ])) + + self.assertEqual(val, [ + ffs(br'1 text-output 0 \x06\x00\x00\x01\x04\x00foo %sval1'), + ]) + + def testtextoutput2arg(self): + val = list(framing.createtextoutputframe(1, [ + (b'foo %s %s', [b'val', b'value'], []), + ])) + + self.assertEqual(val, [ + ffs(br'1 text-output 0 \x09\x00\x00\x02\x03\x00\x05\x00' + br'foo %s %svalvalue'), + ]) + + def testtextoutput1label(self): + val = list(framing.createtextoutputframe(1, [ + (b'foo', [], [b'label']), + ])) + + self.assertEqual(val, [ + ffs(br'1 text-output 0 \x03\x00\x01\x00\x05foolabel'), + ]) + + def testargandlabel(self): + val = list(framing.createtextoutputframe(1, [ + (b'foo %s', [b'arg'], [b'label']), + ])) + + self.assertEqual(val, [ + ffs(br'1 text-output 0 \x06\x00\x01\x01\x05\x03\x00foo %slabelarg'), + ]) + class ServerReactorTests(unittest.TestCase): def _sendsingleframe(self, reactor, s): results = list(sendframes(reactor, [ffs(s)])) diff --git a/mercurial/wireprotoframing.py b/mercurial/wireprotoframing.py --- a/mercurial/wireprotoframing.py +++ b/mercurial/wireprotoframing.py @@ -27,13 +27,15 @@ FRAME_TYPE_COMMAND_DATA = 0x03 FRAME_TYPE_BYTES_RESPONSE = 0x04 FRAME_TYPE_ERROR_RESPONSE = 0x05 +FRAME_TYPE_TEXT_OUTPUT = 0x06 FRAME_TYPES = { b'command-name': FRAME_TYPE_COMMAND_NAME, b'command-argument': FRAME_TYPE_COMMAND_ARGUMENT, b'command-data': FRAME_TYPE_COMMAND_DATA, b'bytes-response': FRAME_TYPE_BYTES_RESPONSE, b'error-response': FRAME_TYPE_ERROR_RESPONSE, + b'text-output': FRAME_TYPE_TEXT_OUTPUT, } FLAG_COMMAND_NAME_EOS = 0x01 @@ -85,6 +87,7 @@ FRAME_TYPE_COMMAND_DATA: FLAGS_COMMAND_DATA, FRAME_TYPE_BYTES_RESPONSE: FLAGS_BYTES_RESPONSE, FRAME_TYPE_ERROR_RESPONSE: FLAGS_ERROR_RESPONSE, + FRAME_TYPE_TEXT_OUTPUT: {}, } ARGUMENT_FRAME_HEADER = struct.Struct(r' 255: + raise ValueError('cannot use more than 255 formatting arguments') + if len(labels) > 255: + raise ValueError('cannot use more than 255 labels') + + # TODO look for localstr, other types here? + + if not isinstance(formatting, bytes): + raise ValueError('must use bytes formatting strings') + for arg in args: + if not isinstance(arg, bytes): + raise ValueError('must use bytes for arguments') + for label in labels: + if not isinstance(label, bytes): + raise ValueError('must use bytes for labels') + + # Formatting string must be UTF-8. + formatting = formatting.decode(r'utf-8', r'replace').encode(r'utf-8') + + # Arguments must be UTF-8. + args = [a.decode(r'utf-8', r'replace').encode(r'utf-8') for a in args] + + # Labels must be ASCII. + labels = [l.decode(r'ascii', r'strict').encode(r'ascii') + for l in labels] + + if len(formatting) > 65535: + raise ValueError('formatting string cannot be longer than 64k') + + if any(len(a) > 65535 for a in args): + raise ValueError('argument string cannot be longer than 64k') + + if any(len(l) > 255 for l in labels): + raise ValueError('label string cannot be longer than 255 bytes') + + chunks = [ + struct.pack(r'