##// END OF EJS Templates
wireprotov2server: use our CBOR encoder...
Gregory Szorc -
r39479:660879e4 default
parent child Browse files
Show More
@@ -1,534 +1,535 b''
1 1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 3 #
4 4 # This software may be used and distributed according to the terms of the
5 5 # GNU General Public License version 2 or any later version.
6 6
7 7 from __future__ import absolute_import
8 8
9 9 import contextlib
10 10
11 11 from .i18n import _
12 from .thirdparty import (
13 cbor,
14 )
15 12 from . import (
16 13 encoding,
17 14 error,
18 15 pycompat,
19 16 streamclone,
20 17 util,
21 18 wireprotoframing,
22 19 wireprototypes,
23 20 )
24 21 from .utils import (
22 cborutil,
25 23 interfaceutil,
26 24 )
27 25
28 26 FRAMINGTYPE = b'application/mercurial-exp-framing-0005'
29 27
30 28 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
31 29
32 30 COMMANDS = wireprototypes.commanddict()
33 31
34 32 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
35 33 from .hgweb import common as hgwebcommon
36 34
37 35 # URL space looks like: <permissions>/<command>, where <permission> can
38 36 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
39 37
40 38 # Root URL does nothing meaningful... yet.
41 39 if not urlparts:
42 40 res.status = b'200 OK'
43 41 res.headers[b'Content-Type'] = b'text/plain'
44 42 res.setbodybytes(_('HTTP version 2 API handler'))
45 43 return
46 44
47 45 if len(urlparts) == 1:
48 46 res.status = b'404 Not Found'
49 47 res.headers[b'Content-Type'] = b'text/plain'
50 48 res.setbodybytes(_('do not know how to process %s\n') %
51 49 req.dispatchpath)
52 50 return
53 51
54 52 permission, command = urlparts[0:2]
55 53
56 54 if permission not in (b'ro', b'rw'):
57 55 res.status = b'404 Not Found'
58 56 res.headers[b'Content-Type'] = b'text/plain'
59 57 res.setbodybytes(_('unknown permission: %s') % permission)
60 58 return
61 59
62 60 if req.method != 'POST':
63 61 res.status = b'405 Method Not Allowed'
64 62 res.headers[b'Allow'] = b'POST'
65 63 res.setbodybytes(_('commands require POST requests'))
66 64 return
67 65
68 66 # At some point we'll want to use our own API instead of recycling the
69 67 # behavior of version 1 of the wire protocol...
70 68 # TODO return reasonable responses - not responses that overload the
71 69 # HTTP status line message for error reporting.
72 70 try:
73 71 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
74 72 except hgwebcommon.ErrorResponse as e:
75 73 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
76 74 for k, v in e.headers:
77 75 res.headers[k] = v
78 76 res.setbodybytes('permission denied')
79 77 return
80 78
81 79 # We have a special endpoint to reflect the request back at the client.
82 80 if command == b'debugreflect':
83 81 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
84 82 return
85 83
86 84 # Extra commands that we handle that aren't really wire protocol
87 85 # commands. Think extra hard before making this hackery available to
88 86 # extension.
89 87 extracommands = {'multirequest'}
90 88
91 89 if command not in COMMANDS and command not in extracommands:
92 90 res.status = b'404 Not Found'
93 91 res.headers[b'Content-Type'] = b'text/plain'
94 92 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
95 93 return
96 94
97 95 repo = rctx.repo
98 96 ui = repo.ui
99 97
100 98 proto = httpv2protocolhandler(req, ui)
101 99
102 100 if (not COMMANDS.commandavailable(command, proto)
103 101 and command not in extracommands):
104 102 res.status = b'404 Not Found'
105 103 res.headers[b'Content-Type'] = b'text/plain'
106 104 res.setbodybytes(_('invalid wire protocol command: %s') % command)
107 105 return
108 106
109 107 # TODO consider cases where proxies may add additional Accept headers.
110 108 if req.headers.get(b'Accept') != FRAMINGTYPE:
111 109 res.status = b'406 Not Acceptable'
112 110 res.headers[b'Content-Type'] = b'text/plain'
113 111 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
114 112 % FRAMINGTYPE)
115 113 return
116 114
117 115 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
118 116 res.status = b'415 Unsupported Media Type'
119 117 # TODO we should send a response with appropriate media type,
120 118 # since client does Accept it.
121 119 res.headers[b'Content-Type'] = b'text/plain'
122 120 res.setbodybytes(_('client MUST send Content-Type header with '
123 121 'value: %s\n') % FRAMINGTYPE)
124 122 return
125 123
126 124 _processhttpv2request(ui, repo, req, res, permission, command, proto)
127 125
128 126 def _processhttpv2reflectrequest(ui, repo, req, res):
129 127 """Reads unified frame protocol request and dumps out state to client.
130 128
131 129 This special endpoint can be used to help debug the wire protocol.
132 130
133 131 Instead of routing the request through the normal dispatch mechanism,
134 132 we instead read all frames, decode them, and feed them into our state
135 133 tracker. We then dump the log of all that activity back out to the
136 134 client.
137 135 """
138 136 import json
139 137
140 138 # Reflection APIs have a history of being abused, accidentally disclosing
141 139 # sensitive data, etc. So we have a config knob.
142 140 if not ui.configbool('experimental', 'web.api.debugreflect'):
143 141 res.status = b'404 Not Found'
144 142 res.headers[b'Content-Type'] = b'text/plain'
145 143 res.setbodybytes(_('debugreflect service not available'))
146 144 return
147 145
148 146 # We assume we have a unified framing protocol request body.
149 147
150 148 reactor = wireprotoframing.serverreactor()
151 149 states = []
152 150
153 151 while True:
154 152 frame = wireprotoframing.readframe(req.bodyfh)
155 153
156 154 if not frame:
157 155 states.append(b'received: <no frame>')
158 156 break
159 157
160 158 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
161 159 frame.requestid,
162 160 frame.payload))
163 161
164 162 action, meta = reactor.onframerecv(frame)
165 163 states.append(json.dumps((action, meta), sort_keys=True,
166 164 separators=(', ', ': ')))
167 165
168 166 action, meta = reactor.oninputeof()
169 167 meta['action'] = action
170 168 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
171 169
172 170 res.status = b'200 OK'
173 171 res.headers[b'Content-Type'] = b'text/plain'
174 172 res.setbodybytes(b'\n'.join(states))
175 173
176 174 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
177 175 """Post-validation handler for HTTPv2 requests.
178 176
179 177 Called when the HTTP request contains unified frame-based protocol
180 178 frames for evaluation.
181 179 """
182 180 # TODO Some HTTP clients are full duplex and can receive data before
183 181 # the entire request is transmitted. Figure out a way to indicate support
184 182 # for that so we can opt into full duplex mode.
185 183 reactor = wireprotoframing.serverreactor(deferoutput=True)
186 184 seencommand = False
187 185
188 186 outstream = reactor.makeoutputstream()
189 187
190 188 while True:
191 189 frame = wireprotoframing.readframe(req.bodyfh)
192 190 if not frame:
193 191 break
194 192
195 193 action, meta = reactor.onframerecv(frame)
196 194
197 195 if action == 'wantframe':
198 196 # Need more data before we can do anything.
199 197 continue
200 198 elif action == 'runcommand':
201 199 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
202 200 reqcommand, reactor, outstream,
203 201 meta, issubsequent=seencommand)
204 202
205 203 if sentoutput:
206 204 return
207 205
208 206 seencommand = True
209 207
210 208 elif action == 'error':
211 209 # TODO define proper error mechanism.
212 210 res.status = b'200 OK'
213 211 res.headers[b'Content-Type'] = b'text/plain'
214 212 res.setbodybytes(meta['message'] + b'\n')
215 213 return
216 214 else:
217 215 raise error.ProgrammingError(
218 216 'unhandled action from frame processor: %s' % action)
219 217
220 218 action, meta = reactor.oninputeof()
221 219 if action == 'sendframes':
222 220 # We assume we haven't started sending the response yet. If we're
223 221 # wrong, the response type will raise an exception.
224 222 res.status = b'200 OK'
225 223 res.headers[b'Content-Type'] = FRAMINGTYPE
226 224 res.setbodygen(meta['framegen'])
227 225 elif action == 'noop':
228 226 pass
229 227 else:
230 228 raise error.ProgrammingError('unhandled action from frame processor: %s'
231 229 % action)
232 230
233 231 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
234 232 outstream, command, issubsequent):
235 233 """Dispatch a wire protocol command made from HTTPv2 requests.
236 234
237 235 The authenticated permission (``authedperm``) along with the original
238 236 command from the URL (``reqcommand``) are passed in.
239 237 """
240 238 # We already validated that the session has permissions to perform the
241 239 # actions in ``authedperm``. In the unified frame protocol, the canonical
242 240 # command to run is expressed in a frame. However, the URL also requested
243 241 # to run a specific command. We need to be careful that the command we
244 242 # run doesn't have permissions requirements greater than what was granted
245 243 # by ``authedperm``.
246 244 #
247 245 # Our rule for this is we only allow one command per HTTP request and
248 246 # that command must match the command in the URL. However, we make
249 247 # an exception for the ``multirequest`` URL. This URL is allowed to
250 248 # execute multiple commands. We double check permissions of each command
251 249 # as it is invoked to ensure there is no privilege escalation.
252 250 # TODO consider allowing multiple commands to regular command URLs
253 251 # iff each command is the same.
254 252
255 253 proto = httpv2protocolhandler(req, ui, args=command['args'])
256 254
257 255 if reqcommand == b'multirequest':
258 256 if not COMMANDS.commandavailable(command['command'], proto):
259 257 # TODO proper error mechanism
260 258 res.status = b'200 OK'
261 259 res.headers[b'Content-Type'] = b'text/plain'
262 260 res.setbodybytes(_('wire protocol command not available: %s') %
263 261 command['command'])
264 262 return True
265 263
266 264 # TODO don't use assert here, since it may be elided by -O.
267 265 assert authedperm in (b'ro', b'rw')
268 266 wirecommand = COMMANDS[command['command']]
269 267 assert wirecommand.permission in ('push', 'pull')
270 268
271 269 if authedperm == b'ro' and wirecommand.permission != 'pull':
272 270 # TODO proper error mechanism
273 271 res.status = b'403 Forbidden'
274 272 res.headers[b'Content-Type'] = b'text/plain'
275 273 res.setbodybytes(_('insufficient permissions to execute '
276 274 'command: %s') % command['command'])
277 275 return True
278 276
279 277 # TODO should we also call checkperm() here? Maybe not if we're going
280 278 # to overhaul that API. The granted scope from the URL check should
281 279 # be good enough.
282 280
283 281 else:
284 282 # Don't allow multiple commands outside of ``multirequest`` URL.
285 283 if issubsequent:
286 284 # TODO proper error mechanism
287 285 res.status = b'200 OK'
288 286 res.headers[b'Content-Type'] = b'text/plain'
289 287 res.setbodybytes(_('multiple commands cannot be issued to this '
290 288 'URL'))
291 289 return True
292 290
293 291 if reqcommand != command['command']:
294 292 # TODO define proper error mechanism
295 293 res.status = b'200 OK'
296 294 res.headers[b'Content-Type'] = b'text/plain'
297 295 res.setbodybytes(_('command in frame must match command in URL'))
298 296 return True
299 297
300 298 rsp = dispatch(repo, proto, command['command'])
301 299
302 300 res.status = b'200 OK'
303 301 res.headers[b'Content-Type'] = FRAMINGTYPE
304 302
303 # TODO consider adding a type to represent an iterable of values to
304 # be CBOR encoded.
305 305 if isinstance(rsp, wireprototypes.cborresponse):
306 encoded = cbor.dumps(rsp.value, canonical=True)
306 # TODO consider calling oncommandresponsereadygen().
307 encoded = b''.join(cborutil.streamencode(rsp.value))
307 308 action, meta = reactor.oncommandresponseready(outstream,
308 309 command['requestid'],
309 310 encoded)
310 311 elif isinstance(rsp, wireprototypes.v2streamingresponse):
311 312 action, meta = reactor.oncommandresponsereadygen(outstream,
312 313 command['requestid'],
313 314 rsp.gen)
314 315 elif isinstance(rsp, wireprototypes.v2errorresponse):
315 316 action, meta = reactor.oncommanderror(outstream,
316 317 command['requestid'],
317 318 rsp.message,
318 319 rsp.args)
319 320 else:
320 321 action, meta = reactor.onservererror(
321 322 _('unhandled response type from wire proto command'))
322 323
323 324 if action == 'sendframes':
324 325 res.setbodygen(meta['framegen'])
325 326 return True
326 327 elif action == 'noop':
327 328 return False
328 329 else:
329 330 raise error.ProgrammingError('unhandled event from reactor: %s' %
330 331 action)
331 332
332 333 def getdispatchrepo(repo, proto, command):
333 334 return repo.filtered('served')
334 335
335 336 def dispatch(repo, proto, command):
336 337 repo = getdispatchrepo(repo, proto, command)
337 338
338 339 func, spec = COMMANDS[command]
339 340 args = proto.getargs(spec)
340 341
341 342 return func(repo, proto, **args)
342 343
343 344 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
344 345 class httpv2protocolhandler(object):
345 346 def __init__(self, req, ui, args=None):
346 347 self._req = req
347 348 self._ui = ui
348 349 self._args = args
349 350
350 351 @property
351 352 def name(self):
352 353 return HTTP_WIREPROTO_V2
353 354
354 355 def getargs(self, args):
355 356 data = {}
356 357 for k, typ in args.items():
357 358 if k == '*':
358 359 raise NotImplementedError('do not support * args')
359 360 elif k in self._args:
360 361 # TODO consider validating value types.
361 362 data[k] = self._args[k]
362 363
363 364 return data
364 365
365 366 def getprotocaps(self):
366 367 # Protocol capabilities are currently not implemented for HTTP V2.
367 368 return set()
368 369
369 370 def getpayload(self):
370 371 raise NotImplementedError
371 372
372 373 @contextlib.contextmanager
373 374 def mayberedirectstdio(self):
374 375 raise NotImplementedError
375 376
376 377 def client(self):
377 378 raise NotImplementedError
378 379
379 380 def addcapabilities(self, repo, caps):
380 381 return caps
381 382
382 383 def checkperm(self, perm):
383 384 raise NotImplementedError
384 385
385 386 def httpv2apidescriptor(req, repo):
386 387 proto = httpv2protocolhandler(req, repo.ui)
387 388
388 389 return _capabilitiesv2(repo, proto)
389 390
390 391 def _capabilitiesv2(repo, proto):
391 392 """Obtain the set of capabilities for version 2 transports.
392 393
393 394 These capabilities are distinct from the capabilities for version 1
394 395 transports.
395 396 """
396 397 compression = []
397 398 for engine in wireprototypes.supportedcompengines(repo.ui, util.SERVERROLE):
398 399 compression.append({
399 400 b'name': engine.wireprotosupport().name,
400 401 })
401 402
402 403 caps = {
403 404 'commands': {},
404 405 'compression': compression,
405 406 'framingmediatypes': [FRAMINGTYPE],
406 407 }
407 408
408 409 for command, entry in COMMANDS.items():
409 410 caps['commands'][command] = {
410 411 'args': entry.args,
411 412 'permissions': [entry.permission],
412 413 }
413 414
414 415 if streamclone.allowservergeneration(repo):
415 416 caps['rawrepoformats'] = sorted(repo.requirements &
416 417 repo.supportedformats)
417 418
418 419 return proto.addcapabilities(repo, caps)
419 420
420 421 def wireprotocommand(name, args=None, permission='push'):
421 422 """Decorator to declare a wire protocol command.
422 423
423 424 ``name`` is the name of the wire protocol command being provided.
424 425
425 426 ``args`` is a dict of argument names to example values.
426 427
427 428 ``permission`` defines the permission type needed to run this command.
428 429 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
429 430 respectively. Default is to assume command requires ``push`` permissions
430 431 because otherwise commands not declaring their permissions could modify
431 432 a repository that is supposed to be read-only.
432 433 """
433 434 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
434 435 if v['version'] == 2}
435 436
436 437 if permission not in ('push', 'pull'):
437 438 raise error.ProgrammingError('invalid wire protocol permission; '
438 439 'got %s; expected "push" or "pull"' %
439 440 permission)
440 441
441 442 if args is None:
442 443 args = {}
443 444
444 445 if not isinstance(args, dict):
445 446 raise error.ProgrammingError('arguments for version 2 commands '
446 447 'must be declared as dicts')
447 448
448 449 def register(func):
449 450 if name in COMMANDS:
450 451 raise error.ProgrammingError('%s command already registered '
451 452 'for version 2' % name)
452 453
453 454 COMMANDS[name] = wireprototypes.commandentry(
454 455 func, args=args, transports=transports, permission=permission)
455 456
456 457 return func
457 458
458 459 return register
459 460
460 461 @wireprotocommand('branchmap', permission='pull')
461 462 def branchmapv2(repo, proto):
462 463 branchmap = {encoding.fromlocal(k): v
463 464 for k, v in repo.branchmap().iteritems()}
464 465
465 466 return wireprototypes.cborresponse(branchmap)
466 467
467 468 @wireprotocommand('capabilities', permission='pull')
468 469 def capabilitiesv2(repo, proto):
469 470 caps = _capabilitiesv2(repo, proto)
470 471
471 472 return wireprototypes.cborresponse(caps)
472 473
473 474 @wireprotocommand('heads',
474 475 args={
475 476 'publiconly': False,
476 477 },
477 478 permission='pull')
478 479 def headsv2(repo, proto, publiconly=False):
479 480 if publiconly:
480 481 repo = repo.filtered('immutable')
481 482
482 483 return wireprototypes.cborresponse(repo.heads())
483 484
484 485 @wireprotocommand('known',
485 486 args={
486 487 'nodes': [b'deadbeef'],
487 488 },
488 489 permission='pull')
489 490 def knownv2(repo, proto, nodes=None):
490 491 nodes = nodes or []
491 492 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
492 493 return wireprototypes.cborresponse(result)
493 494
494 495 @wireprotocommand('listkeys',
495 496 args={
496 497 'namespace': b'ns',
497 498 },
498 499 permission='pull')
499 500 def listkeysv2(repo, proto, namespace=None):
500 501 keys = repo.listkeys(encoding.tolocal(namespace))
501 502 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
502 503 for k, v in keys.iteritems()}
503 504
504 505 return wireprototypes.cborresponse(keys)
505 506
506 507 @wireprotocommand('lookup',
507 508 args={
508 509 'key': b'foo',
509 510 },
510 511 permission='pull')
511 512 def lookupv2(repo, proto, key):
512 513 key = encoding.tolocal(key)
513 514
514 515 # TODO handle exception.
515 516 node = repo.lookup(key)
516 517
517 518 return wireprototypes.cborresponse(node)
518 519
519 520 @wireprotocommand('pushkey',
520 521 args={
521 522 'namespace': b'ns',
522 523 'key': b'key',
523 524 'old': b'old',
524 525 'new': b'new',
525 526 },
526 527 permission='push')
527 528 def pushkeyv2(repo, proto, namespace, key, old, new):
528 529 # TODO handle ui output redirection
529 530 r = repo.pushkey(encoding.tolocal(namespace),
530 531 encoding.tolocal(key),
531 532 encoding.tolocal(old),
532 533 encoding.tolocal(new))
533 534
534 535 return wireprototypes.cborresponse(r)
@@ -1,571 +1,571 b''
1 1 #require no-chg
2 2
3 3 $ . $TESTDIR/wireprotohelpers.sh
4 4 $ enabledummycommands
5 5
6 6 $ hg init server
7 7 $ cat > server/.hg/hgrc << EOF
8 8 > [experimental]
9 9 > web.apiserver = true
10 10 > EOF
11 11 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
12 12 $ cat hg.pid > $DAEMON_PIDS
13 13
14 14 HTTP v2 protocol not enabled by default
15 15
16 16 $ sendhttpraw << EOF
17 17 > httprequest GET api/$HTTPV2
18 18 > user-agent: test
19 19 > EOF
20 20 using raw connection to peer
21 21 s> GET /api/exp-http-v2-0001 HTTP/1.1\r\n
22 22 s> Accept-Encoding: identity\r\n
23 23 s> user-agent: test\r\n
24 24 s> host: $LOCALIP:$HGPORT\r\n (glob)
25 25 s> \r\n
26 26 s> makefile('rb', None)
27 27 s> HTTP/1.1 404 Not Found\r\n
28 28 s> Server: testing stub value\r\n
29 29 s> Date: $HTTP_DATE$\r\n
30 30 s> Content-Type: text/plain\r\n
31 31 s> Content-Length: 33\r\n
32 32 s> \r\n
33 33 s> API exp-http-v2-0001 not enabled\n
34 34
35 35 Restart server with support for HTTP v2 API
36 36
37 37 $ killdaemons.py
38 38 $ enablehttpv2 server
39 39 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
40 40 $ cat hg.pid > $DAEMON_PIDS
41 41
42 42 Request to unknown command yields 404
43 43
44 44 $ sendhttpraw << EOF
45 45 > httprequest POST api/$HTTPV2/ro/badcommand
46 46 > user-agent: test
47 47 > EOF
48 48 using raw connection to peer
49 49 s> POST /api/exp-http-v2-0001/ro/badcommand HTTP/1.1\r\n
50 50 s> Accept-Encoding: identity\r\n
51 51 s> user-agent: test\r\n
52 52 s> host: $LOCALIP:$HGPORT\r\n (glob)
53 53 s> \r\n
54 54 s> makefile('rb', None)
55 55 s> HTTP/1.1 404 Not Found\r\n
56 56 s> Server: testing stub value\r\n
57 57 s> Date: $HTTP_DATE$\r\n
58 58 s> Content-Type: text/plain\r\n
59 59 s> Content-Length: 42\r\n
60 60 s> \r\n
61 61 s> unknown wire protocol command: badcommand\n
62 62
63 63 GET to read-only command yields a 405
64 64
65 65 $ sendhttpraw << EOF
66 66 > httprequest GET api/$HTTPV2/ro/customreadonly
67 67 > user-agent: test
68 68 > EOF
69 69 using raw connection to peer
70 70 s> GET /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
71 71 s> Accept-Encoding: identity\r\n
72 72 s> user-agent: test\r\n
73 73 s> host: $LOCALIP:$HGPORT\r\n (glob)
74 74 s> \r\n
75 75 s> makefile('rb', None)
76 76 s> HTTP/1.1 405 Method Not Allowed\r\n
77 77 s> Server: testing stub value\r\n
78 78 s> Date: $HTTP_DATE$\r\n
79 79 s> Allow: POST\r\n
80 80 s> Content-Length: 30\r\n
81 81 s> \r\n
82 82 s> commands require POST requests
83 83
84 84 Missing Accept header results in 406
85 85
86 86 $ sendhttpraw << EOF
87 87 > httprequest POST api/$HTTPV2/ro/customreadonly
88 88 > user-agent: test
89 89 > EOF
90 90 using raw connection to peer
91 91 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
92 92 s> Accept-Encoding: identity\r\n
93 93 s> user-agent: test\r\n
94 94 s> host: $LOCALIP:$HGPORT\r\n (glob)
95 95 s> \r\n
96 96 s> makefile('rb', None)
97 97 s> HTTP/1.1 406 Not Acceptable\r\n
98 98 s> Server: testing stub value\r\n
99 99 s> Date: $HTTP_DATE$\r\n
100 100 s> Content-Type: text/plain\r\n
101 101 s> Content-Length: 85\r\n
102 102 s> \r\n
103 103 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0005\n
104 104
105 105 Bad Accept header results in 406
106 106
107 107 $ sendhttpraw << EOF
108 108 > httprequest POST api/$HTTPV2/ro/customreadonly
109 109 > accept: invalid
110 110 > user-agent: test
111 111 > EOF
112 112 using raw connection to peer
113 113 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
114 114 s> Accept-Encoding: identity\r\n
115 115 s> accept: invalid\r\n
116 116 s> user-agent: test\r\n
117 117 s> host: $LOCALIP:$HGPORT\r\n (glob)
118 118 s> \r\n
119 119 s> makefile('rb', None)
120 120 s> HTTP/1.1 406 Not Acceptable\r\n
121 121 s> Server: testing stub value\r\n
122 122 s> Date: $HTTP_DATE$\r\n
123 123 s> Content-Type: text/plain\r\n
124 124 s> Content-Length: 85\r\n
125 125 s> \r\n
126 126 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0005\n
127 127
128 128 Bad Content-Type header results in 415
129 129
130 130 $ sendhttpraw << EOF
131 131 > httprequest POST api/$HTTPV2/ro/customreadonly
132 132 > accept: $MEDIATYPE
133 133 > user-agent: test
134 134 > content-type: badmedia
135 135 > EOF
136 136 using raw connection to peer
137 137 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
138 138 s> Accept-Encoding: identity\r\n
139 139 s> accept: application/mercurial-exp-framing-0005\r\n
140 140 s> content-type: badmedia\r\n
141 141 s> user-agent: test\r\n
142 142 s> host: $LOCALIP:$HGPORT\r\n (glob)
143 143 s> \r\n
144 144 s> makefile('rb', None)
145 145 s> HTTP/1.1 415 Unsupported Media Type\r\n
146 146 s> Server: testing stub value\r\n
147 147 s> Date: $HTTP_DATE$\r\n
148 148 s> Content-Type: text/plain\r\n
149 149 s> Content-Length: 88\r\n
150 150 s> \r\n
151 151 s> client MUST send Content-Type header with value: application/mercurial-exp-framing-0005\n
152 152
153 153 Request to read-only command works out of the box
154 154
155 155 $ sendhttpraw << EOF
156 156 > httprequest POST api/$HTTPV2/ro/customreadonly
157 157 > accept: $MEDIATYPE
158 158 > content-type: $MEDIATYPE
159 159 > user-agent: test
160 160 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
161 161 > EOF
162 162 using raw connection to peer
163 163 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
164 164 s> Accept-Encoding: identity\r\n
165 165 s> *\r\n (glob)
166 166 s> content-type: application/mercurial-exp-framing-0005\r\n
167 167 s> user-agent: test\r\n
168 168 s> content-length: 29\r\n
169 169 s> host: $LOCALIP:$HGPORT\r\n (glob)
170 170 s> \r\n
171 171 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
172 172 s> makefile('rb', None)
173 173 s> HTTP/1.1 200 OK\r\n
174 174 s> Server: testing stub value\r\n
175 175 s> Date: $HTTP_DATE$\r\n
176 176 s> Content-Type: application/mercurial-exp-framing-0005\r\n
177 177 s> Transfer-Encoding: chunked\r\n
178 178 s> \r\n
179 179 s> 32\r\n
180 180 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
181 181 s> \r\n
182 182 s> 0\r\n
183 183 s> \r\n
184 184
185 185 $ sendhttpv2peer << EOF
186 186 > command customreadonly
187 187 > EOF
188 188 creating http peer for wire protocol version 2
189 189 sending customreadonly command
190 190 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
191 191 s> Accept-Encoding: identity\r\n
192 192 s> accept: application/mercurial-exp-framing-0005\r\n
193 193 s> content-type: application/mercurial-exp-framing-0005\r\n
194 194 s> content-length: 29\r\n
195 195 s> host: $LOCALIP:$HGPORT\r\n (glob)
196 196 s> user-agent: Mercurial debugwireproto\r\n
197 197 s> \r\n
198 198 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
199 199 s> makefile('rb', None)
200 200 s> HTTP/1.1 200 OK\r\n
201 201 s> Server: testing stub value\r\n
202 202 s> Date: $HTTP_DATE$\r\n
203 203 s> Content-Type: application/mercurial-exp-framing-0005\r\n
204 204 s> Transfer-Encoding: chunked\r\n
205 205 s> \r\n
206 206 s> 32\r\n
207 207 s> *\x00\x00\x01\x00\x02\x012
208 208 s> \xa1FstatusBokX\x1dcustomreadonly bytes response
209 209 s> \r\n
210 210 received frame(size=42; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
211 211 s> 0\r\n
212 212 s> \r\n
213 213 response: [
214 214 {
215 215 b'status': b'ok'
216 216 },
217 217 b'customreadonly bytes response'
218 218 ]
219 219
220 220 Request to read-write command fails because server is read-only by default
221 221
222 222 GET to read-write request yields 405
223 223
224 224 $ sendhttpraw << EOF
225 225 > httprequest GET api/$HTTPV2/rw/customreadonly
226 226 > user-agent: test
227 227 > EOF
228 228 using raw connection to peer
229 229 s> GET /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
230 230 s> Accept-Encoding: identity\r\n
231 231 s> user-agent: test\r\n
232 232 s> host: $LOCALIP:$HGPORT\r\n (glob)
233 233 s> \r\n
234 234 s> makefile('rb', None)
235 235 s> HTTP/1.1 405 Method Not Allowed\r\n
236 236 s> Server: testing stub value\r\n
237 237 s> Date: $HTTP_DATE$\r\n
238 238 s> Allow: POST\r\n
239 239 s> Content-Length: 30\r\n
240 240 s> \r\n
241 241 s> commands require POST requests
242 242
243 243 Even for unknown commands
244 244
245 245 $ sendhttpraw << EOF
246 246 > httprequest GET api/$HTTPV2/rw/badcommand
247 247 > user-agent: test
248 248 > EOF
249 249 using raw connection to peer
250 250 s> GET /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
251 251 s> Accept-Encoding: identity\r\n
252 252 s> user-agent: test\r\n
253 253 s> host: $LOCALIP:$HGPORT\r\n (glob)
254 254 s> \r\n
255 255 s> makefile('rb', None)
256 256 s> HTTP/1.1 405 Method Not Allowed\r\n
257 257 s> Server: testing stub value\r\n
258 258 s> Date: $HTTP_DATE$\r\n
259 259 s> Allow: POST\r\n
260 260 s> Content-Length: 30\r\n
261 261 s> \r\n
262 262 s> commands require POST requests
263 263
264 264 SSL required by default
265 265
266 266 $ sendhttpraw << EOF
267 267 > httprequest POST api/$HTTPV2/rw/customreadonly
268 268 > user-agent: test
269 269 > EOF
270 270 using raw connection to peer
271 271 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
272 272 s> Accept-Encoding: identity\r\n
273 273 s> user-agent: test\r\n
274 274 s> host: $LOCALIP:$HGPORT\r\n (glob)
275 275 s> \r\n
276 276 s> makefile('rb', None)
277 277 s> HTTP/1.1 403 ssl required\r\n
278 278 s> Server: testing stub value\r\n
279 279 s> Date: $HTTP_DATE$\r\n
280 280 s> Content-Length: 17\r\n
281 281 s> \r\n
282 282 s> permission denied
283 283
284 284 Restart server to allow non-ssl read-write operations
285 285
286 286 $ killdaemons.py
287 287 $ cat > server/.hg/hgrc << EOF
288 288 > [experimental]
289 289 > web.apiserver = true
290 290 > web.api.http-v2 = true
291 291 > [web]
292 292 > push_ssl = false
293 293 > allow-push = *
294 294 > EOF
295 295
296 296 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
297 297 $ cat hg.pid > $DAEMON_PIDS
298 298
299 299 Authorized request for valid read-write command works
300 300
301 301 $ sendhttpraw << EOF
302 302 > httprequest POST api/$HTTPV2/rw/customreadonly
303 303 > user-agent: test
304 304 > accept: $MEDIATYPE
305 305 > content-type: $MEDIATYPE
306 306 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
307 307 > EOF
308 308 using raw connection to peer
309 309 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
310 310 s> Accept-Encoding: identity\r\n
311 311 s> accept: application/mercurial-exp-framing-0005\r\n
312 312 s> content-type: application/mercurial-exp-framing-0005\r\n
313 313 s> user-agent: test\r\n
314 314 s> content-length: 29\r\n
315 315 s> host: $LOCALIP:$HGPORT\r\n (glob)
316 316 s> \r\n
317 317 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
318 318 s> makefile('rb', None)
319 319 s> HTTP/1.1 200 OK\r\n
320 320 s> Server: testing stub value\r\n
321 321 s> Date: $HTTP_DATE$\r\n
322 322 s> Content-Type: application/mercurial-exp-framing-0005\r\n
323 323 s> Transfer-Encoding: chunked\r\n
324 324 s> \r\n
325 325 s> 32\r\n
326 326 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
327 327 s> \r\n
328 328 s> 0\r\n
329 329 s> \r\n
330 330
331 331 Authorized request for unknown command is rejected
332 332
333 333 $ sendhttpraw << EOF
334 334 > httprequest POST api/$HTTPV2/rw/badcommand
335 335 > user-agent: test
336 336 > accept: $MEDIATYPE
337 337 > EOF
338 338 using raw connection to peer
339 339 s> POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
340 340 s> Accept-Encoding: identity\r\n
341 341 s> accept: application/mercurial-exp-framing-0005\r\n
342 342 s> user-agent: test\r\n
343 343 s> host: $LOCALIP:$HGPORT\r\n (glob)
344 344 s> \r\n
345 345 s> makefile('rb', None)
346 346 s> HTTP/1.1 404 Not Found\r\n
347 347 s> Server: testing stub value\r\n
348 348 s> Date: $HTTP_DATE$\r\n
349 349 s> Content-Type: text/plain\r\n
350 350 s> Content-Length: 42\r\n
351 351 s> \r\n
352 352 s> unknown wire protocol command: badcommand\n
353 353
354 354 debugreflect isn't enabled by default
355 355
356 356 $ sendhttpraw << EOF
357 357 > httprequest POST api/$HTTPV2/ro/debugreflect
358 358 > user-agent: test
359 359 > EOF
360 360 using raw connection to peer
361 361 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
362 362 s> Accept-Encoding: identity\r\n
363 363 s> user-agent: test\r\n
364 364 s> host: $LOCALIP:$HGPORT\r\n (glob)
365 365 s> \r\n
366 366 s> makefile('rb', None)
367 367 s> HTTP/1.1 404 Not Found\r\n
368 368 s> Server: testing stub value\r\n
369 369 s> Date: $HTTP_DATE$\r\n
370 370 s> Content-Type: text/plain\r\n
371 371 s> Content-Length: 34\r\n
372 372 s> \r\n
373 373 s> debugreflect service not available
374 374
375 375 Restart server to get debugreflect endpoint
376 376
377 377 $ killdaemons.py
378 378 $ cat > server/.hg/hgrc << EOF
379 379 > [experimental]
380 380 > web.apiserver = true
381 381 > web.api.debugreflect = true
382 382 > web.api.http-v2 = true
383 383 > [web]
384 384 > push_ssl = false
385 385 > allow-push = *
386 386 > EOF
387 387
388 388 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
389 389 $ cat hg.pid > $DAEMON_PIDS
390 390
391 391 Command frames can be reflected via debugreflect
392 392
393 393 $ sendhttpraw << EOF
394 394 > httprequest POST api/$HTTPV2/ro/debugreflect
395 395 > accept: $MEDIATYPE
396 396 > content-type: $MEDIATYPE
397 397 > user-agent: test
398 398 > frame 1 1 stream-begin command-request new cbor:{b'name': b'command1', b'args': {b'foo': b'val1', b'bar1': b'val'}}
399 399 > EOF
400 400 using raw connection to peer
401 401 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
402 402 s> Accept-Encoding: identity\r\n
403 403 s> accept: application/mercurial-exp-framing-0005\r\n
404 404 s> content-type: application/mercurial-exp-framing-0005\r\n
405 405 s> user-agent: test\r\n
406 406 s> content-length: 47\r\n
407 407 s> host: $LOCALIP:$HGPORT\r\n (glob)
408 408 s> \r\n
409 409 s> \'\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1
410 410 s> makefile('rb', None)
411 411 s> HTTP/1.1 200 OK\r\n
412 412 s> Server: testing stub value\r\n
413 413 s> Date: $HTTP_DATE$\r\n
414 414 s> Content-Type: text/plain\r\n
415 415 s> Content-Length: 205\r\n
416 416 s> \r\n
417 417 s> received: 1 1 1 \xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1\n
418 418 s> ["runcommand", {"args": {"bar1": "val", "foo": "val1"}, "command": "command1", "data": null, "requestid": 1}]\n
419 419 s> received: <no frame>\n
420 420 s> {"action": "noop"}
421 421
422 422 Multiple requests to regular command URL are not allowed
423 423
424 424 $ sendhttpraw << EOF
425 425 > httprequest POST api/$HTTPV2/ro/customreadonly
426 426 > accept: $MEDIATYPE
427 427 > content-type: $MEDIATYPE
428 428 > user-agent: test
429 429 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
430 430 > EOF
431 431 using raw connection to peer
432 432 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
433 433 s> Accept-Encoding: identity\r\n
434 434 s> accept: application/mercurial-exp-framing-0005\r\n
435 435 s> content-type: application/mercurial-exp-framing-0005\r\n
436 436 s> user-agent: test\r\n
437 437 s> content-length: 29\r\n
438 438 s> host: $LOCALIP:$HGPORT\r\n (glob)
439 439 s> \r\n
440 440 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
441 441 s> makefile('rb', None)
442 442 s> HTTP/1.1 200 OK\r\n
443 443 s> Server: testing stub value\r\n
444 444 s> Date: $HTTP_DATE$\r\n
445 445 s> Content-Type: application/mercurial-exp-framing-0005\r\n
446 446 s> Transfer-Encoding: chunked\r\n
447 447 s> \r\n
448 448 s> 32\r\n
449 449 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
450 450 s> \r\n
451 451 s> 0\r\n
452 452 s> \r\n
453 453
454 454 Multiple requests to "multirequest" URL are allowed
455 455
456 456 $ sendhttpraw << EOF
457 457 > httprequest POST api/$HTTPV2/ro/multirequest
458 458 > accept: $MEDIATYPE
459 459 > content-type: $MEDIATYPE
460 460 > user-agent: test
461 461 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
462 462 > frame 3 1 0 command-request new cbor:{b'name': b'customreadonly'}
463 463 > EOF
464 464 using raw connection to peer
465 465 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
466 466 s> Accept-Encoding: identity\r\n
467 467 s> *\r\n (glob)
468 468 s> *\r\n (glob)
469 469 s> user-agent: test\r\n
470 470 s> content-length: 58\r\n
471 471 s> host: $LOCALIP:$HGPORT\r\n (glob)
472 472 s> \r\n
473 473 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly\x15\x00\x00\x03\x00\x01\x00\x11\xa1DnameNcustomreadonly
474 474 s> makefile('rb', None)
475 475 s> HTTP/1.1 200 OK\r\n
476 476 s> Server: testing stub value\r\n
477 477 s> Date: $HTTP_DATE$\r\n
478 478 s> Content-Type: application/mercurial-exp-framing-0005\r\n
479 479 s> Transfer-Encoding: chunked\r\n
480 480 s> \r\n
481 481 s> 32\r\n
482 482 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
483 483 s> \r\n
484 484 s> 32\r\n
485 485 s> *\x00\x00\x03\x00\x02\x002\xa1FstatusBokX\x1dcustomreadonly bytes response
486 486 s> \r\n
487 487 s> 0\r\n
488 488 s> \r\n
489 489
490 490 Interleaved requests to "multirequest" are processed
491 491
492 492 $ sendhttpraw << EOF
493 493 > httprequest POST api/$HTTPV2/ro/multirequest
494 494 > accept: $MEDIATYPE
495 495 > content-type: $MEDIATYPE
496 496 > user-agent: test
497 497 > frame 1 1 stream-begin command-request new|more \xa2Dargs\xa1Inamespace
498 498 > frame 3 1 0 command-request new|more \xa2Dargs\xa1Inamespace
499 499 > frame 3 1 0 command-request continuation JnamespacesDnameHlistkeys
500 500 > frame 1 1 0 command-request continuation IbookmarksDnameHlistkeys
501 501 > EOF
502 502 using raw connection to peer
503 503 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
504 504 s> Accept-Encoding: identity\r\n
505 505 s> accept: application/mercurial-exp-framing-0005\r\n
506 506 s> content-type: application/mercurial-exp-framing-0005\r\n
507 507 s> user-agent: test\r\n
508 508 s> content-length: 115\r\n
509 509 s> host: $LOCALIP:$HGPORT\r\n (glob)
510 510 s> \r\n
511 511 s> \x11\x00\x00\x01\x00\x01\x01\x15\xa2Dargs\xa1Inamespace\x11\x00\x00\x03\x00\x01\x00\x15\xa2Dargs\xa1Inamespace\x19\x00\x00\x03\x00\x01\x00\x12JnamespacesDnameHlistkeys\x18\x00\x00\x01\x00\x01\x00\x12IbookmarksDnameHlistkeys
512 512 s> makefile('rb', None)
513 513 s> HTTP/1.1 200 OK\r\n
514 514 s> Server: testing stub value\r\n
515 515 s> Date: $HTTP_DATE$\r\n
516 516 s> Content-Type: application/mercurial-exp-framing-0005\r\n
517 517 s> Transfer-Encoding: chunked\r\n
518 518 s> \r\n
519 519 s> 33\r\n
520 s> +\x00\x00\x03\x00\x02\x012\xa1FstatusBok\xa3Fphases@Ibookmarks@Jnamespaces@
520 s> +\x00\x00\x03\x00\x02\x012\xa1FstatusBok\xa3Ibookmarks@Jnamespaces@Fphases@
521 521 s> \r\n
522 522 s> 14\r\n
523 523 s> \x0c\x00\x00\x01\x00\x02\x002\xa1FstatusBok\xa0
524 524 s> \r\n
525 525 s> 0\r\n
526 526 s> \r\n
527 527
528 528 Restart server to disable read-write access
529 529
530 530 $ killdaemons.py
531 531 $ cat > server/.hg/hgrc << EOF
532 532 > [experimental]
533 533 > web.apiserver = true
534 534 > web.api.debugreflect = true
535 535 > web.api.http-v2 = true
536 536 > [web]
537 537 > push_ssl = false
538 538 > EOF
539 539
540 540 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
541 541 $ cat hg.pid > $DAEMON_PIDS
542 542
543 543 Attempting to run a read-write command via multirequest on read-only URL is not allowed
544 544
545 545 $ sendhttpraw << EOF
546 546 > httprequest POST api/$HTTPV2/ro/multirequest
547 547 > accept: $MEDIATYPE
548 548 > content-type: $MEDIATYPE
549 549 > user-agent: test
550 550 > frame 1 1 stream-begin command-request new cbor:{b'name': b'pushkey'}
551 551 > EOF
552 552 using raw connection to peer
553 553 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
554 554 s> Accept-Encoding: identity\r\n
555 555 s> accept: application/mercurial-exp-framing-0005\r\n
556 556 s> content-type: application/mercurial-exp-framing-0005\r\n
557 557 s> user-agent: test\r\n
558 558 s> content-length: 22\r\n
559 559 s> host: $LOCALIP:$HGPORT\r\n (glob)
560 560 s> \r\n
561 561 s> \x0e\x00\x00\x01\x00\x01\x01\x11\xa1DnameGpushkey
562 562 s> makefile('rb', None)
563 563 s> HTTP/1.1 403 Forbidden\r\n
564 564 s> Server: testing stub value\r\n
565 565 s> Date: $HTTP_DATE$\r\n
566 566 s> Content-Type: text/plain\r\n
567 567 s> Content-Length: 52\r\n
568 568 s> \r\n
569 569 s> insufficient permissions to execute command: pushkey
570 570
571 571 $ cat error.log
@@ -1,422 +1,422 b''
1 1 #require no-chg
2 2
3 3 $ . $TESTDIR/wireprotohelpers.sh
4 4
5 5 $ hg init server
6 6
7 7 zstd isn't present in plain builds. Make tests easier by removing
8 8 zstd from the equation.
9 9
10 10 $ cat >> server/.hg/hgrc << EOF
11 11 > [server]
12 12 > compressionengines = zlib
13 13 > EOF
14 14
15 15 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
16 16 $ cat hg.pid > $DAEMON_PIDS
17 17
18 18 A normal capabilities request is serviced for version 1
19 19
20 20 $ sendhttpraw << EOF
21 21 > httprequest GET ?cmd=capabilities
22 22 > user-agent: test
23 23 > EOF
24 24 using raw connection to peer
25 25 s> GET /?cmd=capabilities HTTP/1.1\r\n
26 26 s> Accept-Encoding: identity\r\n
27 27 s> user-agent: test\r\n
28 28 s> host: $LOCALIP:$HGPORT\r\n (glob)
29 29 s> \r\n
30 30 s> makefile('rb', None)
31 31 s> HTTP/1.1 200 Script output follows\r\n
32 32 s> Server: testing stub value\r\n
33 33 s> Date: $HTTP_DATE$\r\n
34 34 s> Content-Type: application/mercurial-0.1\r\n
35 35 s> Content-Length: *\r\n (glob)
36 36 s> \r\n
37 37 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
38 38
39 39 A proper request without the API server enabled returns the legacy response
40 40
41 41 $ sendhttpraw << EOF
42 42 > httprequest GET ?cmd=capabilities
43 43 > user-agent: test
44 44 > x-hgupgrade-1: foo
45 45 > x-hgproto-1: cbor
46 46 > EOF
47 47 using raw connection to peer
48 48 s> GET /?cmd=capabilities HTTP/1.1\r\n
49 49 s> Accept-Encoding: identity\r\n
50 50 s> user-agent: test\r\n
51 51 s> x-hgproto-1: cbor\r\n
52 52 s> x-hgupgrade-1: foo\r\n
53 53 s> host: $LOCALIP:$HGPORT\r\n (glob)
54 54 s> \r\n
55 55 s> makefile('rb', None)
56 56 s> HTTP/1.1 200 Script output follows\r\n
57 57 s> Server: testing stub value\r\n
58 58 s> Date: $HTTP_DATE$\r\n
59 59 s> Content-Type: application/mercurial-0.1\r\n
60 60 s> Content-Length: *\r\n (glob)
61 61 s> \r\n
62 62 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
63 63
64 64 Restart with just API server enabled. This enables serving the new format.
65 65
66 66 $ killdaemons.py
67 67 $ cat error.log
68 68
69 69 $ cat >> server/.hg/hgrc << EOF
70 70 > [experimental]
71 71 > web.apiserver = true
72 72 > EOF
73 73
74 74 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
75 75 $ cat hg.pid > $DAEMON_PIDS
76 76
77 77 X-HgUpgrade-<N> without CBOR advertisement uses legacy response
78 78
79 79 $ sendhttpraw << EOF
80 80 > httprequest GET ?cmd=capabilities
81 81 > user-agent: test
82 82 > x-hgupgrade-1: foo bar
83 83 > EOF
84 84 using raw connection to peer
85 85 s> GET /?cmd=capabilities HTTP/1.1\r\n
86 86 s> Accept-Encoding: identity\r\n
87 87 s> user-agent: test\r\n
88 88 s> x-hgupgrade-1: foo bar\r\n
89 89 s> host: $LOCALIP:$HGPORT\r\n (glob)
90 90 s> \r\n
91 91 s> makefile('rb', None)
92 92 s> HTTP/1.1 200 Script output follows\r\n
93 93 s> Server: testing stub value\r\n
94 94 s> Date: $HTTP_DATE$\r\n
95 95 s> Content-Type: application/mercurial-0.1\r\n
96 96 s> Content-Length: *\r\n (glob)
97 97 s> \r\n
98 98 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
99 99
100 100 X-HgUpgrade-<N> without known serialization in X-HgProto-<N> uses legacy response
101 101
102 102 $ sendhttpraw << EOF
103 103 > httprequest GET ?cmd=capabilities
104 104 > user-agent: test
105 105 > x-hgupgrade-1: foo bar
106 106 > x-hgproto-1: some value
107 107 > EOF
108 108 using raw connection to peer
109 109 s> GET /?cmd=capabilities HTTP/1.1\r\n
110 110 s> Accept-Encoding: identity\r\n
111 111 s> user-agent: test\r\n
112 112 s> x-hgproto-1: some value\r\n
113 113 s> x-hgupgrade-1: foo bar\r\n
114 114 s> host: $LOCALIP:$HGPORT\r\n (glob)
115 115 s> \r\n
116 116 s> makefile('rb', None)
117 117 s> HTTP/1.1 200 Script output follows\r\n
118 118 s> Server: testing stub value\r\n
119 119 s> Date: $HTTP_DATE$\r\n
120 120 s> Content-Type: application/mercurial-0.1\r\n
121 121 s> Content-Length: *\r\n (glob)
122 122 s> \r\n
123 123 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
124 124
125 125 X-HgUpgrade-<N> + X-HgProto-<N> headers trigger new response format
126 126
127 127 $ sendhttpraw << EOF
128 128 > httprequest GET ?cmd=capabilities
129 129 > user-agent: test
130 130 > x-hgupgrade-1: foo bar
131 131 > x-hgproto-1: cbor
132 132 > EOF
133 133 using raw connection to peer
134 134 s> GET /?cmd=capabilities HTTP/1.1\r\n
135 135 s> Accept-Encoding: identity\r\n
136 136 s> user-agent: test\r\n
137 137 s> x-hgproto-1: cbor\r\n
138 138 s> x-hgupgrade-1: foo bar\r\n
139 139 s> host: $LOCALIP:$HGPORT\r\n (glob)
140 140 s> \r\n
141 141 s> makefile('rb', None)
142 142 s> HTTP/1.1 200 OK\r\n
143 143 s> Server: testing stub value\r\n
144 144 s> Date: $HTTP_DATE$\r\n
145 145 s> Content-Type: application/mercurial-cbor\r\n
146 146 s> Content-Length: *\r\n (glob)
147 147 s> \r\n
148 148 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
149 149 cbor> {
150 150 b'apibase': b'api/',
151 151 b'apis': {},
152 152 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
153 153 }
154 154
155 155 Restart server to enable HTTPv2
156 156
157 157 $ killdaemons.py
158 158 $ enablehttpv2 server
159 159 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
160 160 $ cat hg.pid > $DAEMON_PIDS
161 161
162 162 Only requested API services are returned
163 163
164 164 $ sendhttpraw << EOF
165 165 > httprequest GET ?cmd=capabilities
166 166 > user-agent: test
167 167 > x-hgupgrade-1: foo bar
168 168 > x-hgproto-1: cbor
169 169 > EOF
170 170 using raw connection to peer
171 171 s> GET /?cmd=capabilities HTTP/1.1\r\n
172 172 s> Accept-Encoding: identity\r\n
173 173 s> user-agent: test\r\n
174 174 s> x-hgproto-1: cbor\r\n
175 175 s> x-hgupgrade-1: foo bar\r\n
176 176 s> host: $LOCALIP:$HGPORT\r\n (glob)
177 177 s> \r\n
178 178 s> makefile('rb', None)
179 179 s> HTTP/1.1 200 OK\r\n
180 180 s> Server: testing stub value\r\n
181 181 s> Date: $HTTP_DATE$\r\n
182 182 s> Content-Type: application/mercurial-cbor\r\n
183 183 s> Content-Length: *\r\n (glob)
184 184 s> \r\n
185 185 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
186 186 cbor> {
187 187 b'apibase': b'api/',
188 188 b'apis': {},
189 189 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
190 190 }
191 191
192 192 Request for HTTPv2 service returns information about it
193 193
194 194 $ sendhttpraw << EOF
195 195 > httprequest GET ?cmd=capabilities
196 196 > user-agent: test
197 197 > x-hgupgrade-1: exp-http-v2-0001 foo bar
198 198 > x-hgproto-1: cbor
199 199 > EOF
200 200 using raw connection to peer
201 201 s> GET /?cmd=capabilities HTTP/1.1\r\n
202 202 s> Accept-Encoding: identity\r\n
203 203 s> user-agent: test\r\n
204 204 s> x-hgproto-1: cbor\r\n
205 205 s> x-hgupgrade-1: exp-http-v2-0001 foo bar\r\n
206 206 s> host: $LOCALIP:$HGPORT\r\n (glob)
207 207 s> \r\n
208 208 s> makefile('rb', None)
209 209 s> HTTP/1.1 200 OK\r\n
210 210 s> Server: testing stub value\r\n
211 211 s> Date: $HTTP_DATE$\r\n
212 212 s> Content-Type: application/mercurial-cbor\r\n
213 213 s> Content-Length: *\r\n (glob)
214 214 s> \r\n
215 215 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
216 216 cbor> {
217 217 b'apibase': b'api/',
218 218 b'apis': {
219 219 b'exp-http-v2-0001': {
220 220 b'commands': {
221 221 b'branchmap': {
222 222 b'args': {},
223 223 b'permissions': [
224 224 b'pull'
225 225 ]
226 226 },
227 227 b'capabilities': {
228 228 b'args': {},
229 229 b'permissions': [
230 230 b'pull'
231 231 ]
232 232 },
233 233 b'heads': {
234 234 b'args': {
235 235 b'publiconly': False
236 236 },
237 237 b'permissions': [
238 238 b'pull'
239 239 ]
240 240 },
241 241 b'known': {
242 242 b'args': {
243 243 b'nodes': [
244 244 b'deadbeef'
245 245 ]
246 246 },
247 247 b'permissions': [
248 248 b'pull'
249 249 ]
250 250 },
251 251 b'listkeys': {
252 252 b'args': {
253 253 b'namespace': b'ns'
254 254 },
255 255 b'permissions': [
256 256 b'pull'
257 257 ]
258 258 },
259 259 b'lookup': {
260 260 b'args': {
261 261 b'key': b'foo'
262 262 },
263 263 b'permissions': [
264 264 b'pull'
265 265 ]
266 266 },
267 267 b'pushkey': {
268 268 b'args': {
269 269 b'key': b'key',
270 270 b'namespace': b'ns',
271 271 b'new': b'new',
272 272 b'old': b'old'
273 273 },
274 274 b'permissions': [
275 275 b'push'
276 276 ]
277 277 }
278 278 },
279 279 b'compression': [
280 280 {
281 281 b'name': b'zlib'
282 282 }
283 283 ],
284 284 b'framingmediatypes': [
285 285 b'application/mercurial-exp-framing-0005'
286 286 ],
287 287 b'rawrepoformats': [
288 288 b'generaldelta',
289 289 b'revlogv1'
290 290 ]
291 291 }
292 292 },
293 293 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
294 294 }
295 295
296 296 capabilities command returns expected info
297 297
298 298 $ sendhttpv2peerhandshake << EOF
299 299 > command capabilities
300 300 > EOF
301 301 creating http peer for wire protocol version 2
302 302 s> GET /?cmd=capabilities HTTP/1.1\r\n
303 303 s> Accept-Encoding: identity\r\n
304 304 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
305 305 s> x-hgproto-1: cbor\r\n
306 306 s> x-hgupgrade-1: exp-http-v2-0001\r\n
307 307 s> accept: application/mercurial-0.1\r\n
308 308 s> host: $LOCALIP:$HGPORT\r\n (glob)
309 309 s> user-agent: Mercurial debugwireproto\r\n
310 310 s> \r\n
311 311 s> makefile('rb', None)
312 312 s> HTTP/1.1 200 OK\r\n
313 313 s> Server: testing stub value\r\n
314 314 s> Date: $HTTP_DATE$\r\n
315 315 s> Content-Type: application/mercurial-cbor\r\n
316 316 s> Content-Length: *\r\n (glob)
317 317 s> \r\n
318 318 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
319 319 sending capabilities command
320 320 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
321 321 s> Accept-Encoding: identity\r\n
322 322 s> *\r\n (glob)
323 323 s> content-type: application/mercurial-exp-framing-0005\r\n
324 324 s> content-length: 27\r\n
325 325 s> host: $LOCALIP:$HGPORT\r\n (glob)
326 326 s> user-agent: Mercurial debugwireproto\r\n
327 327 s> \r\n
328 328 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
329 329 s> makefile('rb', None)
330 330 s> HTTP/1.1 200 OK\r\n
331 331 s> Server: testing stub value\r\n
332 332 s> Date: $HTTP_DATE$\r\n
333 333 s> Content-Type: application/mercurial-exp-framing-0005\r\n
334 334 s> Transfer-Encoding: chunked\r\n
335 335 s> \r\n
336 336 s> 1d7\r\n
337 337 s> \xcf\x01\x00\x01\x00\x02\x012
338 s> \xa1FstatusBok\xa4Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x81\xa1DnameDzlibNrawrepoformats\x82LgeneraldeltaHrevlogv1Qframingmediatypes\x81X&application/mercurial-exp-framing-0005
338 s> \xa1FstatusBok\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1
339 339 s> \r\n
340 340 received frame(size=463; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
341 341 s> 0\r\n
342 342 s> \r\n
343 343 response: [
344 344 {
345 345 b'status': b'ok'
346 346 },
347 347 {
348 348 b'commands': {
349 349 b'branchmap': {
350 350 b'args': {},
351 351 b'permissions': [
352 352 b'pull'
353 353 ]
354 354 },
355 355 b'capabilities': {
356 356 b'args': {},
357 357 b'permissions': [
358 358 b'pull'
359 359 ]
360 360 },
361 361 b'heads': {
362 362 b'args': {
363 363 b'publiconly': False
364 364 },
365 365 b'permissions': [
366 366 b'pull'
367 367 ]
368 368 },
369 369 b'known': {
370 370 b'args': {
371 371 b'nodes': [
372 372 b'deadbeef'
373 373 ]
374 374 },
375 375 b'permissions': [
376 376 b'pull'
377 377 ]
378 378 },
379 379 b'listkeys': {
380 380 b'args': {
381 381 b'namespace': b'ns'
382 382 },
383 383 b'permissions': [
384 384 b'pull'
385 385 ]
386 386 },
387 387 b'lookup': {
388 388 b'args': {
389 389 b'key': b'foo'
390 390 },
391 391 b'permissions': [
392 392 b'pull'
393 393 ]
394 394 },
395 395 b'pushkey': {
396 396 b'args': {
397 397 b'key': b'key',
398 398 b'namespace': b'ns',
399 399 b'new': b'new',
400 400 b'old': b'old'
401 401 },
402 402 b'permissions': [
403 403 b'push'
404 404 ]
405 405 }
406 406 },
407 407 b'compression': [
408 408 {
409 409 b'name': b'zlib'
410 410 }
411 411 ],
412 412 b'framingmediatypes': [
413 413 b'application/mercurial-exp-framing-0005'
414 414 ],
415 415 b'rawrepoformats': [
416 416 b'generaldelta',
417 417 b'revlogv1'
418 418 ]
419 419 }
420 420 ]
421 421
422 422 $ cat error.log
@@ -1,134 +1,134 b''
1 1 $ . $TESTDIR/wireprotohelpers.sh
2 2
3 3 $ hg init server
4 4 $ enablehttpv2 server
5 5 $ cd server
6 6 $ hg debugdrawdag << EOF
7 7 > C D
8 8 > |/
9 9 > B
10 10 > |
11 11 > A
12 12 > EOF
13 13
14 14 $ hg phase --public -r C
15 15 $ hg book -r C @
16 16
17 17 $ hg log -T '{rev}:{node} {desc}\n'
18 18 3:be0ef73c17ade3fc89dc41701eb9fc3a91b58282 D
19 19 2:26805aba1e600a82e93661149f2313866a221a7b C
20 20 1:112478962961147124edd43549aedd1a335e44bf B
21 21 0:426bada5c67598ca65036d57d9e4b64b0c1ce7a0 A
22 22
23 23 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
24 24 $ cat hg.pid > $DAEMON_PIDS
25 25
26 26 Request for namespaces works
27 27
28 28 $ sendhttpv2peer << EOF
29 29 > command listkeys
30 30 > namespace namespaces
31 31 > EOF
32 32 creating http peer for wire protocol version 2
33 33 sending listkeys command
34 34 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
35 35 s> Accept-Encoding: identity\r\n
36 36 s> accept: application/mercurial-exp-framing-0005\r\n
37 37 s> content-type: application/mercurial-exp-framing-0005\r\n
38 38 s> content-length: 50\r\n
39 39 s> host: $LOCALIP:$HGPORT\r\n (glob)
40 40 s> user-agent: Mercurial debugwireproto\r\n
41 41 s> \r\n
42 42 s> *\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceJnamespacesDnameHlistkeys
43 43 s> makefile('rb', None)
44 44 s> HTTP/1.1 200 OK\r\n
45 45 s> Server: testing stub value\r\n
46 46 s> Date: $HTTP_DATE$\r\n
47 47 s> Content-Type: application/mercurial-exp-framing-0005\r\n
48 48 s> Transfer-Encoding: chunked\r\n
49 49 s> \r\n
50 50 s> 33\r\n
51 51 s> +\x00\x00\x01\x00\x02\x012
52 s> \xa1FstatusBok\xa3Fphases@Ibookmarks@Jnamespaces@
52 s> \xa1FstatusBok\xa3Ibookmarks@Jnamespaces@Fphases@
53 53 s> \r\n
54 54 received frame(size=43; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
55 55 s> 0\r\n
56 56 s> \r\n
57 57 response: {
58 58 b'bookmarks': b'',
59 59 b'namespaces': b'',
60 60 b'phases': b''
61 61 }
62 62
63 63 Request for phases works
64 64
65 65 $ sendhttpv2peer << EOF
66 66 > command listkeys
67 67 > namespace phases
68 68 > EOF
69 69 creating http peer for wire protocol version 2
70 70 sending listkeys command
71 71 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
72 72 s> Accept-Encoding: identity\r\n
73 73 s> accept: application/mercurial-exp-framing-0005\r\n
74 74 s> content-type: application/mercurial-exp-framing-0005\r\n
75 75 s> content-length: 46\r\n
76 76 s> host: $LOCALIP:$HGPORT\r\n (glob)
77 77 s> user-agent: Mercurial debugwireproto\r\n
78 78 s> \r\n
79 79 s> &\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceFphasesDnameHlistkeys
80 80 s> makefile('rb', None)
81 81 s> HTTP/1.1 200 OK\r\n
82 82 s> Server: testing stub value\r\n
83 83 s> Date: $HTTP_DATE$\r\n
84 84 s> Content-Type: application/mercurial-exp-framing-0005\r\n
85 85 s> Transfer-Encoding: chunked\r\n
86 86 s> \r\n
87 87 s> 50\r\n
88 88 s> H\x00\x00\x01\x00\x02\x012
89 s> \xa1FstatusBok\xa2JpublishingDTrueX(be0ef73c17ade3fc89dc41701eb9fc3a91b58282A1
89 s> \xa1FstatusBok\xa2X(be0ef73c17ade3fc89dc41701eb9fc3a91b58282A1JpublishingDTrue
90 90 s> \r\n
91 91 received frame(size=72; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
92 92 s> 0\r\n
93 93 s> \r\n
94 94 response: {
95 95 b'be0ef73c17ade3fc89dc41701eb9fc3a91b58282': b'1',
96 96 b'publishing': b'True'
97 97 }
98 98
99 99 Request for bookmarks works
100 100
101 101 $ sendhttpv2peer << EOF
102 102 > command listkeys
103 103 > namespace bookmarks
104 104 > EOF
105 105 creating http peer for wire protocol version 2
106 106 sending listkeys command
107 107 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
108 108 s> Accept-Encoding: identity\r\n
109 109 s> accept: application/mercurial-exp-framing-0005\r\n
110 110 s> content-type: application/mercurial-exp-framing-0005\r\n
111 111 s> content-length: 49\r\n
112 112 s> host: $LOCALIP:$HGPORT\r\n (glob)
113 113 s> user-agent: Mercurial debugwireproto\r\n
114 114 s> \r\n
115 115 s> )\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceIbookmarksDnameHlistkeys
116 116 s> makefile('rb', None)
117 117 s> HTTP/1.1 200 OK\r\n
118 118 s> Server: testing stub value\r\n
119 119 s> Date: $HTTP_DATE$\r\n
120 120 s> Content-Type: application/mercurial-exp-framing-0005\r\n
121 121 s> Transfer-Encoding: chunked\r\n
122 122 s> \r\n
123 123 s> 40\r\n
124 124 s> 8\x00\x00\x01\x00\x02\x012
125 125 s> \xa1FstatusBok\xa1A@X(26805aba1e600a82e93661149f2313866a221a7b
126 126 s> \r\n
127 127 received frame(size=56; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
128 128 s> 0\r\n
129 129 s> \r\n
130 130 response: {
131 131 b'@': b'26805aba1e600a82e93661149f2313866a221a7b'
132 132 }
133 133
134 134 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now