##// END OF EJS Templates
wireproto: port heads command to wire protocol v2...
Gregory Szorc -
r37503:0b7475ea default
parent child Browse files
Show More
@@ -0,0 +1,96
1 $ . $TESTDIR/wireprotohelpers.sh
2
3 $ hg init server
4 $ enablehttpv2 server
5 $ cd server
6 $ hg debugdrawdag << EOF
7 > H I J
8 > | | |
9 > E F G
10 > | |/
11 > C D
12 > |/
13 > B
14 > |
15 > A
16 > EOF
17
18 $ hg phase --force --secret J
19 $ hg phase --public E
20
21 $ hg log -r 'E + H + I + G + J' -T '{rev}:{node} {desc} {phase}\n'
22 4:78d2dca436b2f5b188ac267e29b81e07266d38fc E public
23 7:ae492e36b0c8339ffaf328d00b85b4525de1165e H draft
24 8:1d6f6b91d44aaba6d5e580bc30a9948530dbe00b I draft
25 6:29446d2dc5419c5f97447a8bc062e4cc328bf241 G draft
26 9:dec04b246d7cbb670c6689806c05ad17c835284e J secret
27
28 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
29 $ cat hg.pid > $DAEMON_PIDS
30
31 All non-secret heads returned by default
32
33 $ sendhttpv2peer << EOF
34 > command heads
35 > EOF
36 creating http peer for wire protocol version 2
37 sending heads command
38 s> POST /api/exp-http-v2-0001/ro/heads HTTP/1.1\r\n
39 s> Accept-Encoding: identity\r\n
40 s> accept: application/mercurial-exp-framing-0003\r\n
41 s> content-type: application/mercurial-exp-framing-0003\r\n
42 s> content-length: 20\r\n
43 s> host: $LOCALIP:$HGPORT\r\n (glob)
44 s> user-agent: Mercurial debugwireproto\r\n
45 s> \r\n
46 s> \x0c\x00\x00\x01\x00\x01\x01\x11\xa1DnameEheads
47 s> makefile('rb', None)
48 s> HTTP/1.1 200 OK\r\n
49 s> Server: testing stub value\r\n
50 s> Date: $HTTP_DATE$\r\n
51 s> Content-Type: application/mercurial-exp-framing-0003\r\n
52 s> Transfer-Encoding: chunked\r\n
53 s> \r\n
54 s> 48\r\n
55 s> @\x00\x00\x01\x00\x02\x01F
56 s> \x83T\x1dok\x91\xd4J\xab\xa6\xd5\xe5\x80\xbc0\xa9\x94\x850\xdb\xe0\x0bT\xaeI.6\xb0\xc83\x9f\xfa\xf3(\xd0\x0b\x85\xb4R]\xe1\x16^T)Dm-\xc5A\x9c_\x97Dz\x8b\xc0b\xe4\xcc2\x8b\xf2A
57 s> \r\n
58 received frame(size=64; request=1; stream=2; streamflags=stream-begin; type=bytes-response; flags=eos|cbor)
59 s> 0\r\n
60 s> \r\n
61 response: [[b'\x1dok\x91\xd4J\xab\xa6\xd5\xe5\x80\xbc0\xa9\x94\x850\xdb\xe0\x0b', b'\xaeI.6\xb0\xc83\x9f\xfa\xf3(\xd0\x0b\x85\xb4R]\xe1\x16^', b')Dm-\xc5A\x9c_\x97Dz\x8b\xc0b\xe4\xcc2\x8b\xf2A']]
62
63 Requesting just the public heads works
64
65 $ sendhttpv2peer << EOF
66 > command heads
67 > publiconly 1
68 > EOF
69 creating http peer for wire protocol version 2
70 sending heads command
71 s> POST /api/exp-http-v2-0001/ro/heads HTTP/1.1\r\n
72 s> Accept-Encoding: identity\r\n
73 s> accept: application/mercurial-exp-framing-0003\r\n
74 s> content-type: application/mercurial-exp-framing-0003\r\n
75 s> content-length: 39\r\n
76 s> host: $LOCALIP:$HGPORT\r\n (glob)
77 s> user-agent: Mercurial debugwireproto\r\n
78 s> \r\n
79 s> \x1f\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1JpubliconlyA1DnameEheads
80 s> makefile('rb', None)
81 s> HTTP/1.1 200 OK\r\n
82 s> Server: testing stub value\r\n
83 s> Date: $HTTP_DATE$\r\n
84 s> Content-Type: application/mercurial-exp-framing-0003\r\n
85 s> Transfer-Encoding: chunked\r\n
86 s> \r\n
87 s> 1e\r\n
88 s> \x16\x00\x00\x01\x00\x02\x01F
89 s> \x81Tx\xd2\xdc\xa46\xb2\xf5\xb1\x88\xac&~)\xb8\x1e\x07&m8\xfc
90 s> \r\n
91 received frame(size=22; request=1; stream=2; streamflags=stream-begin; type=bytes-response; flags=eos|cbor)
92 s> 0\r\n
93 s> \r\n
94 response: [[b'x\xd2\xdc\xa46\xb2\xf5\xb1\x88\xac&~)\xb8\x1e\x07&m8\xfc']]
95
96 $ cat error.log
@@ -1649,3 +1649,40 response is zlib compressed.
1649
1649
1650 The server may also respond with a generic error type, which contains a string
1650 The server may also respond with a generic error type, which contains a string
1651 indicating the failure.
1651 indicating the failure.
1652
1653 Frame-Based Protocol Commands
1654 =============================
1655
1656 **Experimental and under active development**
1657
1658 This section documents the wire protocol commands exposed to transports
1659 using the frame-based protocol. The set of commands exposed through
1660 these transports is distinct from the set of commands exposed to legacy
1661 transports.
1662
1663 The frame-based protocol uses CBOR to encode command execution requests.
1664 All command arguments must be mapped to a specific or set of CBOR data
1665 types.
1666
1667 The response to many commands is also CBOR. There is no common response
1668 format: each command defines its own response format.
1669
1670 TODO require node type be specified, as N bytes of binary node value
1671 could be ambiguous once SHA-1 is replaced.
1672
1673 heads
1674 -----
1675
1676 Obtain DAG heads in the repository.
1677
1678 The command accepts the following arguments:
1679
1680 publiconly (optional)
1681 (boolean) If set, operate on the DAG for public phase changesets only.
1682 Non-public (i.e. draft) phase DAG heads will not be returned.
1683
1684 The response is a CBOR array of bytestrings defining changeset nodes
1685 of DAG heads. The array can be empty if the repository is empty or no
1686 changesets satisfied the request.
1687
1688 TODO consider exposing phase of heads in response
@@ -518,7 +518,15 def dispatch(repo, proto, command):
518 func, spec = commandtable[command]
518 func, spec = commandtable[command]
519
519
520 args = proto.getargs(spec)
520 args = proto.getargs(spec)
521
522 # Version 1 protocols define arguments as a list. Version 2 uses a dict.
523 if isinstance(args, list):
521 return func(repo, proto, *args)
524 return func(repo, proto, *args)
525 elif isinstance(args, dict):
526 return func(repo, proto, **args)
527 else:
528 raise error.ProgrammingError('unexpected type returned from '
529 'proto.getargs(): %s' % type(args))
522
530
523 def options(cmd, keys, others):
531 def options(cmd, keys, others):
524 opts = {}
532 opts = {}
@@ -996,7 +1004,7 def getbundle(repo, proto, others):
996 return wireprototypes.streamres(
1004 return wireprototypes.streamres(
997 gen=chunks, prefer_uncompressed=not prefercompressed)
1005 gen=chunks, prefer_uncompressed=not prefercompressed)
998
1006
999 @wireprotocommand('heads', permission='pull')
1007 @wireprotocommand('heads', permission='pull', transportpolicy=POLICY_V1_ONLY)
1000 def heads(repo, proto):
1008 def heads(repo, proto):
1001 h = repo.heads()
1009 h = repo.heads()
1002 return wireprototypes.bytesresponse(encodelist(h) + '\n')
1010 return wireprototypes.bytesresponse(encodelist(h) + '\n')
@@ -1197,3 +1205,13 def unbundle(repo, proto, heads):
1197 bundler.newpart('error:pushraced',
1205 bundler.newpart('error:pushraced',
1198 [('message', stringutil.forcebytestr(exc))])
1206 [('message', stringutil.forcebytestr(exc))])
1199 return wireprototypes.streamreslegacy(gen=bundler.getchunks())
1207 return wireprototypes.streamreslegacy(gen=bundler.getchunks())
1208
1209 # Wire protocol version 2 commands only past this point.
1210
1211 @wireprotocommand('heads', args='publiconly', permission='pull',
1212 transportpolicy=POLICY_V2_ONLY)
1213 def headsv2(repo, proto, publiconly=False):
1214 if publiconly:
1215 repo = repo.filtered('immutable')
1216
1217 return wireprototypes.cborresponse(repo.heads())
@@ -349,7 +349,7 def createcommandframes(stream, requesti
349 if done:
349 if done:
350 break
350 break
351
351
352 def createbytesresponseframesfrombytes(stream, requestid, data,
352 def createbytesresponseframesfrombytes(stream, requestid, data, iscbor=False,
353 maxframesize=DEFAULT_MAX_FRAME_SIZE):
353 maxframesize=DEFAULT_MAX_FRAME_SIZE):
354 """Create a raw frame to send a bytes response from static bytes input.
354 """Create a raw frame to send a bytes response from static bytes input.
355
355
@@ -358,9 +358,13 def createbytesresponseframesfrombytes(s
358
358
359 # Simple case of a single frame.
359 # Simple case of a single frame.
360 if len(data) <= maxframesize:
360 if len(data) <= maxframesize:
361 flags = FLAG_BYTES_RESPONSE_EOS
362 if iscbor:
363 flags |= FLAG_BYTES_RESPONSE_CBOR
364
361 yield stream.makeframe(requestid=requestid,
365 yield stream.makeframe(requestid=requestid,
362 typeid=FRAME_TYPE_BYTES_RESPONSE,
366 typeid=FRAME_TYPE_BYTES_RESPONSE,
363 flags=FLAG_BYTES_RESPONSE_EOS,
367 flags=flags,
364 payload=data)
368 payload=data)
365 return
369 return
366
370
@@ -375,6 +379,9 def createbytesresponseframesfrombytes(s
375 else:
379 else:
376 flags = FLAG_BYTES_RESPONSE_CONTINUATION
380 flags = FLAG_BYTES_RESPONSE_CONTINUATION
377
381
382 if iscbor:
383 flags |= FLAG_BYTES_RESPONSE_CBOR
384
378 yield stream.makeframe(requestid=requestid,
385 yield stream.makeframe(requestid=requestid,
379 typeid=FRAME_TYPE_BYTES_RESPONSE,
386 typeid=FRAME_TYPE_BYTES_RESPONSE,
380 flags=flags,
387 flags=flags,
@@ -608,7 +615,7 class serverreactor(object):
608
615
609 return meth(frame)
616 return meth(frame)
610
617
611 def onbytesresponseready(self, stream, requestid, data):
618 def onbytesresponseready(self, stream, requestid, data, iscbor=False):
612 """Signal that a bytes response is ready to be sent to the client.
619 """Signal that a bytes response is ready to be sent to the client.
613
620
614 The raw bytes response is passed as an argument.
621 The raw bytes response is passed as an argument.
@@ -617,7 +624,8 class serverreactor(object):
617
624
618 def sendframes():
625 def sendframes():
619 for frame in createbytesresponseframesfrombytes(stream, requestid,
626 for frame in createbytesresponseframesfrombytes(stream, requestid,
620 data):
627 data,
628 iscbor=iscbor):
621 yield frame
629 yield frame
622
630
623 self._activecommands.remove(requestid)
631 self._activecommands.remove(requestid)
@@ -12,6 +12,9 import sys
12 import threading
12 import threading
13
13
14 from .i18n import _
14 from .i18n import _
15 from .thirdparty import (
16 cbor,
17 )
15 from .thirdparty.zope import (
18 from .thirdparty.zope import (
16 interface as zi,
19 interface as zi,
17 )
20 )
@@ -563,6 +566,12 def _httpv2runcommand(ui, repo, req, res
563 action, meta = reactor.onbytesresponseready(outstream,
566 action, meta = reactor.onbytesresponseready(outstream,
564 command['requestid'],
567 command['requestid'],
565 rsp.data)
568 rsp.data)
569 elif isinstance(rsp, wireprototypes.cborresponse):
570 encoded = cbor.dumps(rsp.value, canonical=True)
571 action, meta = reactor.onbytesresponseready(outstream,
572 command['requestid'],
573 encoded,
574 iscbor=True)
566 else:
575 else:
567 action, meta = reactor.onapplicationerror(
576 action, meta = reactor.onapplicationerror(
568 _('unhandled response type from wire proto command'))
577 _('unhandled response type from wire proto command'))
@@ -600,10 +609,10 class httpv2protocolhandler(object):
600 for k in args.split():
609 for k in args.split():
601 if k == '*':
610 if k == '*':
602 raise NotImplementedError('do not support * args')
611 raise NotImplementedError('do not support * args')
603 else:
612 elif k in self._args:
604 data[k] = self._args[k]
613 data[k] = self._args[k]
605
614
606 return [data[k] for k in args.split()]
615 return data
607
616
608 def getprotocaps(self):
617 def getprotocaps(self):
609 # Protocol capabilities are currently not implemented for HTTP V2.
618 # Protocol capabilities are currently not implemented for HTTP V2.
@@ -97,6 +97,11 class streamreslegacy(object):
97 def __init__(self, gen=None):
97 def __init__(self, gen=None):
98 self.gen = gen
98 self.gen = gen
99
99
100 class cborresponse(object):
101 """Encode the response value as CBOR."""
102 def __init__(self, v):
103 self.value = v
104
100 class baseprotocolhandler(zi.Interface):
105 class baseprotocolhandler(zi.Interface):
101 """Abstract base class for wire protocol handlers.
106 """Abstract base class for wire protocol handlers.
102
107
@@ -115,7 +120,10 class baseprotocolhandler(zi.Interface):
115 def getargs(args):
120 def getargs(args):
116 """return the value for arguments in <args>
121 """return the value for arguments in <args>
117
122
118 returns a list of values (same order as <args>)"""
123 For version 1 transports, returns a list of values in the same
124 order they appear in ``args``. For version 2 transports, returns
125 a dict mapping argument name to value.
126 """
119
127
120 def getprotocaps():
128 def getprotocaps():
121 """Returns the list of protocol-level capabilities of client
129 """Returns the list of protocol-level capabilities of client
General Comments 0
You need to be logged in to leave comments. Login now