comparison mercurial/wireprotoframing.py @ 37290:cc5a040fe150

wireproto: syntax for encoding CBOR into frames We just vendored a library for encoding and decoding the CBOR data format. While the intent of that vendor was to support state files, CBOR is really a nice data format. It is extensible and compact. I've been feeling dirty inventing my own data formats for frame payloads. While custom formats can always beat out a generic format, there is a cost to be paid in terms of implementation, comprehension, etc. CBOR is compact enough that I'm not too worried about efficiency loss. I think the benefits of using a standardized format outweigh rolling our own formats. So I plan to make heavy use of CBOR in the wire protocol going forward. This commit introduces support for encoding CBOR data in frame payloads to our function to make a frame from a human string. We do need to employ some low-level Python code in order to evaluate a string as a Python expression. But other than that, this should hopefully be pretty straightforward. Unit tests for this function have been added. Differential Revision: https://phab.mercurial-scm.org/D2948
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 28 Mar 2018 15:05:39 -0700
parents 5fadc63ac99f
children b0041036214e
comparison
equal deleted inserted replaced
37289:5fadc63ac99f 37290:cc5a040fe150
14 import struct 14 import struct
15 15
16 from .i18n import _ 16 from .i18n import _
17 from .thirdparty import ( 17 from .thirdparty import (
18 attr, 18 attr,
19 cbor,
19 ) 20 )
20 from . import ( 21 from . import (
21 error, 22 error,
22 util, 23 util,
23 ) 24 )
154 return frame 155 return frame
155 156
156 def makeframefromhumanstring(s): 157 def makeframefromhumanstring(s):
157 """Create a frame from a human readable string 158 """Create a frame from a human readable string
158 159
160 DANGER: NOT SAFE TO USE WITH UNTRUSTED INPUT BECAUSE OF POTENTIAL
161 eval() USAGE. DO NOT USE IN CORE.
162
159 Strings have the form: 163 Strings have the form:
160 164
161 <request-id> <stream-id> <stream-flags> <type> <flags> <payload> 165 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
162 166
163 This can be used by user-facing applications and tests for creating 167 This can be used by user-facing applications and tests for creating
167 171
168 Stream flags, frame type, and flags can be specified by integer or 172 Stream flags, frame type, and flags can be specified by integer or
169 named constant. 173 named constant.
170 174
171 Flags can be delimited by `|` to bitwise OR them together. 175 Flags can be delimited by `|` to bitwise OR them together.
176
177 If the payload begins with ``cbor:``, the following string will be
178 evaluated as Python code and the resulting object will be fed into
179 a CBOR encoder. Otherwise, the payload is interpreted as a Python
180 byte string literal.
172 """ 181 """
173 fields = s.split(b' ', 5) 182 fields = s.split(b' ', 5)
174 requestid, streamid, streamflags, frametype, frameflags, payload = fields 183 requestid, streamid, streamflags, frametype, frameflags, payload = fields
175 184
176 requestid = int(requestid) 185 requestid = int(requestid)
194 if flag in validflags: 203 if flag in validflags:
195 finalflags |= validflags[flag] 204 finalflags |= validflags[flag]
196 else: 205 else:
197 finalflags |= int(flag) 206 finalflags |= int(flag)
198 207
199 payload = stringutil.unescapestr(payload) 208 if payload.startswith(b'cbor:'):
209 payload = cbor.dumps(stringutil.evalpython(payload[5:]), canonical=True)
210
211 else:
212 payload = stringutil.unescapestr(payload)
200 213
201 return makeframe(requestid=requestid, streamid=streamid, 214 return makeframe(requestid=requestid, streamid=streamid,
202 streamflags=finalstreamflags, typeid=frametype, 215 streamflags=finalstreamflags, typeid=frametype,
203 flags=finalflags, payload=payload) 216 flags=finalflags, payload=payload)
204 217