Mercurial > public > mercurial-scm > hg
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'], |