Mercurial > public > mercurial-scm > hg
comparison mercurial/wireprotoframing.py @ 37726:0c184ca594bb
wireprotov2: change behavior of error frame
Now that we have a leading CBOR map in command response frames
to indicate overall command result status, we don't need to use
the error response frame to represent command errors. Instead,
we can reserve it for protocol and server level errors. And for the
special case of a command error that occurred after command response
frames were emitted.
The code for error handling still needs a ton of work. But we're
slowly going in the right direction...
Differential Revision: https://phab.mercurial-scm.org/D3386
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sat, 14 Apr 2018 15:36:12 -0700 |
parents | 3ea8323d6f95 |
children | 564a3eec6e63 |
comparison
equal
deleted
inserted
replaced
37725:3ea8323d6f95 | 37726:0c184ca594bb |
---|---|
85 FLAGS_COMMAND_RESPONSE = { | 85 FLAGS_COMMAND_RESPONSE = { |
86 b'continuation': FLAG_COMMAND_RESPONSE_CONTINUATION, | 86 b'continuation': FLAG_COMMAND_RESPONSE_CONTINUATION, |
87 b'eos': FLAG_COMMAND_RESPONSE_EOS, | 87 b'eos': FLAG_COMMAND_RESPONSE_EOS, |
88 } | 88 } |
89 | 89 |
90 FLAG_ERROR_RESPONSE_PROTOCOL = 0x01 | |
91 FLAG_ERROR_RESPONSE_APPLICATION = 0x02 | |
92 | |
93 FLAGS_ERROR_RESPONSE = { | |
94 b'protocol': FLAG_ERROR_RESPONSE_PROTOCOL, | |
95 b'application': FLAG_ERROR_RESPONSE_APPLICATION, | |
96 } | |
97 | |
98 # Maps frame types to their available flags. | 90 # Maps frame types to their available flags. |
99 FRAME_TYPE_FLAGS = { | 91 FRAME_TYPE_FLAGS = { |
100 FRAME_TYPE_COMMAND_REQUEST: FLAGS_COMMAND_REQUEST, | 92 FRAME_TYPE_COMMAND_REQUEST: FLAGS_COMMAND_REQUEST, |
101 FRAME_TYPE_COMMAND_DATA: FLAGS_COMMAND_DATA, | 93 FRAME_TYPE_COMMAND_DATA: FLAGS_COMMAND_DATA, |
102 FRAME_TYPE_COMMAND_RESPONSE: FLAGS_COMMAND_RESPONSE, | 94 FRAME_TYPE_COMMAND_RESPONSE: FLAGS_COMMAND_RESPONSE, |
103 FRAME_TYPE_ERROR_RESPONSE: FLAGS_ERROR_RESPONSE, | 95 FRAME_TYPE_ERROR_RESPONSE: {}, |
104 FRAME_TYPE_TEXT_OUTPUT: {}, | 96 FRAME_TYPE_TEXT_OUTPUT: {}, |
105 FRAME_TYPE_PROGRESS: {}, | 97 FRAME_TYPE_PROGRESS: {}, |
106 FRAME_TYPE_STREAM_SETTINGS: {}, | 98 FRAME_TYPE_STREAM_SETTINGS: {}, |
107 } | 99 } |
108 | 100 |
392 payload=chunk) | 384 payload=chunk) |
393 | 385 |
394 if done: | 386 if done: |
395 break | 387 break |
396 | 388 |
397 def createerrorframe(stream, requestid, msg, protocol=False, application=False): | 389 def createerrorframe(stream, requestid, msg, errtype): |
398 # TODO properly handle frame size limits. | 390 # TODO properly handle frame size limits. |
399 assert len(msg) <= DEFAULT_MAX_FRAME_SIZE | 391 assert len(msg) <= DEFAULT_MAX_FRAME_SIZE |
400 | 392 |
401 flags = 0 | 393 payload = cbor.dumps({ |
402 if protocol: | 394 b'type': errtype, |
403 flags |= FLAG_ERROR_RESPONSE_PROTOCOL | 395 b'message': [{b'msg': msg}], |
404 if application: | 396 }, canonical=True) |
405 flags |= FLAG_ERROR_RESPONSE_APPLICATION | |
406 | 397 |
407 yield stream.makeframe(requestid=requestid, | 398 yield stream.makeframe(requestid=requestid, |
408 typeid=FRAME_TYPE_ERROR_RESPONSE, | 399 typeid=FRAME_TYPE_ERROR_RESPONSE, |
409 flags=flags, | 400 flags=0, |
410 payload=msg) | 401 payload=payload) |
411 | 402 |
412 def createtextoutputframe(stream, requestid, atoms, | 403 def createtextoutputframe(stream, requestid, atoms, |
413 maxframesize=DEFAULT_MAX_FRAME_SIZE): | 404 maxframesize=DEFAULT_MAX_FRAME_SIZE): |
414 """Create a text output frame to render text to people. | 405 """Create a text output frame to render text to people. |
415 | 406 |
662 | 653 |
663 return 'sendframes', { | 654 return 'sendframes', { |
664 'framegen': makegen(), | 655 'framegen': makegen(), |
665 } | 656 } |
666 | 657 |
667 def onapplicationerror(self, stream, requestid, msg): | 658 def onservererror(self, stream, requestid, msg): |
668 ensureserverstream(stream) | 659 ensureserverstream(stream) |
669 | 660 |
670 return 'sendframes', { | 661 return 'sendframes', { |
671 'framegen': createerrorframe(stream, requestid, msg, | 662 'framegen': createerrorframe(stream, requestid, msg, |
672 application=True), | 663 errtype='server'), |
673 } | 664 } |
674 | 665 |
675 def makeoutputstream(self): | 666 def makeoutputstream(self): |
676 """Create a stream to be used for sending data to the client.""" | 667 """Create a stream to be used for sending data to the client.""" |
677 streamid = self._nextoutgoingstreamid | 668 streamid = self._nextoutgoingstreamid |
1049 request = self._activerequests[frame.requestid] | 1040 request = self._activerequests[frame.requestid] |
1050 request.state = 'receiving' | 1041 request.state = 'receiving' |
1051 | 1042 |
1052 handlers = { | 1043 handlers = { |
1053 FRAME_TYPE_COMMAND_RESPONSE: self._oncommandresponseframe, | 1044 FRAME_TYPE_COMMAND_RESPONSE: self._oncommandresponseframe, |
1045 FRAME_TYPE_ERROR_RESPONSE: self._onerrorresponseframe, | |
1054 } | 1046 } |
1055 | 1047 |
1056 meth = handlers.get(frame.typeid) | 1048 meth = handlers.get(frame.typeid) |
1057 if not meth: | 1049 if not meth: |
1058 raise error.ProgrammingError('unhandled frame type: %d' % | 1050 raise error.ProgrammingError('unhandled frame type: %d' % |
1069 'request': request, | 1061 'request': request, |
1070 'expectmore': frame.flags & FLAG_COMMAND_RESPONSE_CONTINUATION, | 1062 'expectmore': frame.flags & FLAG_COMMAND_RESPONSE_CONTINUATION, |
1071 'eos': frame.flags & FLAG_COMMAND_RESPONSE_EOS, | 1063 'eos': frame.flags & FLAG_COMMAND_RESPONSE_EOS, |
1072 'data': frame.payload, | 1064 'data': frame.payload, |
1073 } | 1065 } |
1066 | |
1067 def _onerrorresponseframe(self, request, frame): | |
1068 request.state = 'errored' | |
1069 del self._activerequests[request.requestid] | |
1070 | |
1071 # The payload should be a CBOR map. | |
1072 m = cbor.loads(frame.payload) | |
1073 | |
1074 return 'error', { | |
1075 'request': request, | |
1076 'type': m['type'], | |
1077 'message': m['message'], | |
1078 } |