comparison mercurial/wireprotoframing.py @ 39446:36f487a332ad

wireprotoframing: use our CBOR module Tests changed because our CBOR encoder appears to sort map keys differently from the vendored CBOR package. The CBOR specification does define canonical sorting rules for keys based on the byte values. I'm guessing our implementation doesn't follow them. But our encoder doesn't guarantee that it conforms with the canonical specification. Right now, we just care that output is deterministic. And our encoder does guarantee that. Differential Revision: https://phab.mercurial-scm.org/D4466
author Gregory Szorc <gregory.szorc@gmail.com>
date Tue, 28 Aug 2018 18:05:08 -0700
parents 564a3eec6e63
children 43d92d68ac88
comparison
equal deleted inserted replaced
39445:cdb56f295b03 39446:36f487a332ad
15 import struct 15 import struct
16 16
17 from .i18n import _ 17 from .i18n import _
18 from .thirdparty import ( 18 from .thirdparty import (
19 attr, 19 attr,
20 cbor,
21 ) 20 )
22 from . import ( 21 from . import (
23 encoding, 22 encoding,
24 error, 23 error,
25 util, 24 util,
26 ) 25 )
27 from .utils import ( 26 from .utils import (
27 cborutil,
28 stringutil, 28 stringutil,
29 ) 29 )
30 30
31 FRAME_HEADER_SIZE = 8 31 FRAME_HEADER_SIZE = 8
32 DEFAULT_MAX_FRAME_SIZE = 32768 32 DEFAULT_MAX_FRAME_SIZE = 32768
215 finalflags |= validflags[flag] 215 finalflags |= validflags[flag]
216 else: 216 else:
217 finalflags |= int(flag) 217 finalflags |= int(flag)
218 218
219 if payload.startswith(b'cbor:'): 219 if payload.startswith(b'cbor:'):
220 payload = cbor.dumps(stringutil.evalpythonliteral(payload[5:]), 220 payload = b''.join(cborutil.streamencode(
221 canonical=True) 221 stringutil.evalpythonliteral(payload[5:])))
222 222
223 else: 223 else:
224 payload = stringutil.unescapestr(payload) 224 payload = stringutil.unescapestr(payload)
225 225
226 return makeframe(requestid=requestid, streamid=streamid, 226 return makeframe(requestid=requestid, streamid=streamid,
287 """ 287 """
288 data = {b'name': cmd} 288 data = {b'name': cmd}
289 if args: 289 if args:
290 data[b'args'] = args 290 data[b'args'] = args
291 291
292 data = cbor.dumps(data, canonical=True) 292 data = b''.join(cborutil.streamencode(data))
293 293
294 offset = 0 294 offset = 0
295 295
296 while True: 296 while True:
297 flags = 0 297 flags = 0
345 """Create a raw frame to send a bytes response from static bytes input. 345 """Create a raw frame to send a bytes response from static bytes input.
346 346
347 Returns a generator of bytearrays. 347 Returns a generator of bytearrays.
348 """ 348 """
349 # Automatically send the overall CBOR response map. 349 # Automatically send the overall CBOR response map.
350 overall = cbor.dumps({b'status': b'ok'}, canonical=True) 350 overall = b''.join(cborutil.streamencode({b'status': b'ok'}))
351 if len(overall) > maxframesize: 351 if len(overall) > maxframesize:
352 raise error.ProgrammingError('not yet implemented') 352 raise error.ProgrammingError('not yet implemented')
353 353
354 # Simple case where we can fit the full response in a single frame. 354 # Simple case where we can fit the full response in a single frame.
355 if len(overall) + len(data) <= maxframesize: 355 if len(overall) + len(data) <= maxframesize:
386 if done: 386 if done:
387 break 387 break
388 388
389 def createbytesresponseframesfromgen(stream, requestid, gen, 389 def createbytesresponseframesfromgen(stream, requestid, gen,
390 maxframesize=DEFAULT_MAX_FRAME_SIZE): 390 maxframesize=DEFAULT_MAX_FRAME_SIZE):
391 overall = cbor.dumps({b'status': b'ok'}, canonical=True) 391 overall = b''.join(cborutil.streamencode({b'status': b'ok'}))
392 392
393 yield stream.makeframe(requestid=requestid, 393 yield stream.makeframe(requestid=requestid,
394 typeid=FRAME_TYPE_COMMAND_RESPONSE, 394 typeid=FRAME_TYPE_COMMAND_RESPONSE,
395 flags=FLAG_COMMAND_RESPONSE_CONTINUATION, 395 flags=FLAG_COMMAND_RESPONSE_CONTINUATION,
396 payload=overall) 396 payload=overall)
427 } 427 }
428 428
429 if args: 429 if args:
430 m[b'error'][b'args'] = args 430 m[b'error'][b'args'] = args
431 431
432 overall = cbor.dumps(m, canonical=True) 432 overall = b''.join(cborutil.streamencode(m))
433 433
434 yield stream.makeframe(requestid=requestid, 434 yield stream.makeframe(requestid=requestid,
435 typeid=FRAME_TYPE_COMMAND_RESPONSE, 435 typeid=FRAME_TYPE_COMMAND_RESPONSE,
436 flags=FLAG_COMMAND_RESPONSE_EOS, 436 flags=FLAG_COMMAND_RESPONSE_EOS,
437 payload=overall) 437 payload=overall)
438 438
439 def createerrorframe(stream, requestid, msg, errtype): 439 def createerrorframe(stream, requestid, msg, errtype):
440 # TODO properly handle frame size limits. 440 # TODO properly handle frame size limits.
441 assert len(msg) <= DEFAULT_MAX_FRAME_SIZE 441 assert len(msg) <= DEFAULT_MAX_FRAME_SIZE
442 442
443 payload = cbor.dumps({ 443 payload = b''.join(cborutil.streamencode({
444 b'type': errtype, 444 b'type': errtype,
445 b'message': [{b'msg': msg}], 445 b'message': [{b'msg': msg}],
446 }, canonical=True) 446 }))
447 447
448 yield stream.makeframe(requestid=requestid, 448 yield stream.makeframe(requestid=requestid,
449 typeid=FRAME_TYPE_ERROR_RESPONSE, 449 typeid=FRAME_TYPE_ERROR_RESPONSE,
450 flags=0, 450 flags=0,
451 payload=payload) 451 payload=payload)
491 if labels: 491 if labels:
492 atom[b'labels'] = labels 492 atom[b'labels'] = labels
493 493
494 atomdicts.append(atom) 494 atomdicts.append(atom)
495 495
496 payload = cbor.dumps(atomdicts, canonical=True) 496 payload = b''.join(cborutil.streamencode(atomdicts))
497 497
498 if len(payload) > maxframesize: 498 if len(payload) > maxframesize:
499 raise ValueError('cannot encode data in a single frame') 499 raise ValueError('cannot encode data in a single frame')
500 500
501 yield stream.makeframe(requestid=requestid, 501 yield stream.makeframe(requestid=requestid,
782 else: 782 else:
783 self._state = 'idle' 783 self._state = 'idle'
784 784
785 # Decode the payloads as CBOR. 785 # Decode the payloads as CBOR.
786 entry['payload'].seek(0) 786 entry['payload'].seek(0)
787 request = cbor.load(entry['payload']) 787 request = cborutil.decodeall(entry['payload'].getvalue())[0]
788 788
789 if b'name' not in request: 789 if b'name' not in request:
790 self._state = 'errored' 790 self._state = 'errored'
791 return self._makeerrorresult( 791 return self._makeerrorresult(
792 _('command request missing "name" field')) 792 _('command request missing "name" field'))
1156 def _onerrorresponseframe(self, request, frame): 1156 def _onerrorresponseframe(self, request, frame):
1157 request.state = 'errored' 1157 request.state = 'errored'
1158 del self._activerequests[request.requestid] 1158 del self._activerequests[request.requestid]
1159 1159
1160 # The payload should be a CBOR map. 1160 # The payload should be a CBOR map.
1161 m = cbor.loads(frame.payload) 1161 m = cborutil.decodeall(frame.payload)[0]
1162 1162
1163 return 'error', { 1163 return 'error', {
1164 'request': request, 1164 'request': request,
1165 'type': m['type'], 1165 'type': m['type'],
1166 'message': m['message'], 1166 'message': m['message'],