##// END OF EJS Templates
wireprotov2: define and implement "manifestdata" command...
Gregory Szorc -
r39673:c7a7c7e8 default
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (507 lines changed) Show them Hide them
@@ -0,0 +1,507 b''
1 $ . $TESTDIR/wireprotohelpers.sh
2
3 $ hg init server
4 $ enablehttpv2 server
5 $ cd server
6 $ echo a0 > a
7 $ echo b0 > b
8 $ mkdir -p dir0/child0 dir0/child1 dir1
9 $ echo c0 > dir0/c
10 $ echo d0 > dir0/d
11 $ echo e0 > dir0/child0/e
12 $ echo f0 > dir0/child1/f
13 $ hg -q commit -A -m 'commit 0'
14
15 $ echo a1 > a
16 $ echo d1 > dir0/d
17 $ hg commit -m 'commit 1'
18 $ echo f0 > dir0/child1/f
19 $ hg commit -m 'commit 2'
20 nothing changed
21 [1]
22
23 $ hg -q up -r 0
24 $ echo a2 > a
25 $ hg commit -m 'commit 3'
26 created new head
27
28 $ hg log -G -T '{rev}:{node} {desc}\n'
29 @ 2:c8757a2ffe552850d1e0dfe60d295ebf64c196d9 commit 3
30 |
31 | o 1:650165e803375748a94df471e5b58d85763e0b29 commit 1
32 |/
33 o 0:6d85ca1270b377d320098556ba5bfad34a9ee12d commit 0
34
35
36 $ hg --debug debugindex -m
37 rev linkrev nodeid p1 p2
38 0 0 1b175b595f022cfab5b809cc0ed551bd0b3ff5e4 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
39 1 1 91e0bdbfb0dde0023fa063edc1445f207a22eac7 1b175b595f022cfab5b809cc0ed551bd0b3ff5e4 0000000000000000000000000000000000000000
40 2 2 46a6721b5edaf0ea04b79a5cb3218854a4d2aba0 1b175b595f022cfab5b809cc0ed551bd0b3ff5e4 0000000000000000000000000000000000000000
41
42 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
43 $ cat hg.pid > $DAEMON_PIDS
44
45 Missing arguments is an error
46
47 $ sendhttpv2peer << EOF
48 > command manifestdata
49 > EOF
50 creating http peer for wire protocol version 2
51 sending manifestdata command
52 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
53 s> Accept-Encoding: identity\r\n
54 s> accept: application/mercurial-exp-framing-0005\r\n
55 s> content-type: application/mercurial-exp-framing-0005\r\n
56 s> content-length: 27\r\n
57 s> host: $LOCALIP:$HGPORT\r\n (glob)
58 s> user-agent: Mercurial debugwireproto\r\n
59 s> \r\n
60 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLmanifestdata
61 s> makefile('rb', None)
62 s> HTTP/1.1 200 OK\r\n
63 s> Server: testing stub value\r\n
64 s> Date: $HTTP_DATE$\r\n
65 s> Content-Type: application/mercurial-exp-framing-0005\r\n
66 s> Transfer-Encoding: chunked\r\n
67 s> \r\n
68 s> 45\r\n
69 s> =\x00\x00\x01\x00\x02\x012
70 s> \xa2Eerror\xa1GmessageX\x1enodes argument must be definedFstatusEerror
71 s> \r\n
72 received frame(size=61; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
73 s> 0\r\n
74 s> \r\n
75 abort: nodes argument must be defined!
76 [255]
77
78 $ sendhttpv2peer << EOF
79 > command manifestdata
80 > nodes eval:[]
81 > EOF
82 creating http peer for wire protocol version 2
83 sending manifestdata command
84 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
85 s> Accept-Encoding: identity\r\n
86 s> accept: application/mercurial-exp-framing-0005\r\n
87 s> content-type: application/mercurial-exp-framing-0005\r\n
88 s> content-length: 40\r\n
89 s> host: $LOCALIP:$HGPORT\r\n (glob)
90 s> user-agent: Mercurial debugwireproto\r\n
91 s> \r\n
92 s> \x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1Enodes\x80DnameLmanifestdata
93 s> makefile('rb', None)
94 s> HTTP/1.1 200 OK\r\n
95 s> Server: testing stub value\r\n
96 s> Date: $HTTP_DATE$\r\n
97 s> Content-Type: application/mercurial-exp-framing-0005\r\n
98 s> Transfer-Encoding: chunked\r\n
99 s> \r\n
100 s> 44\r\n
101 s> <\x00\x00\x01\x00\x02\x012
102 s> \xa2Eerror\xa1GmessageX\x1dtree argument must be definedFstatusEerror
103 s> \r\n
104 received frame(size=60; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
105 s> 0\r\n
106 s> \r\n
107 abort: tree argument must be defined!
108 [255]
109
110 Unknown node is an error
111
112 $ sendhttpv2peer << EOF
113 > command manifestdata
114 > nodes eval:[b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa']
115 > tree eval:b''
116 > EOF
117 creating http peer for wire protocol version 2
118 sending manifestdata command
119 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
120 s> Accept-Encoding: identity\r\n
121 s> accept: application/mercurial-exp-framing-0005\r\n
122 s> content-type: application/mercurial-exp-framing-0005\r\n
123 s> content-length: 67\r\n
124 s> host: $LOCALIP:$HGPORT\r\n (glob)
125 s> user-agent: Mercurial debugwireproto\r\n
126 s> \r\n
127 s> ;\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa2Enodes\x81T\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaaDtree@DnameLmanifestdata
128 s> makefile('rb', None)
129 s> HTTP/1.1 200 OK\r\n
130 s> Server: testing stub value\r\n
131 s> Date: $HTTP_DATE$\r\n
132 s> Content-Type: application/mercurial-exp-framing-0005\r\n
133 s> Transfer-Encoding: chunked\r\n
134 s> \r\n
135 s> 51\r\n
136 s> I\x00\x00\x01\x00\x02\x012
137 s> \xa2Eerror\xa2Dargs\x81T\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaaGmessagePunknown node: %sFstatusEerror
138 s> \r\n
139 received frame(size=73; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
140 s> 0\r\n
141 s> \r\n
142 abort: unknown node: \xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa! (esc)
143 [255]
144
145 Fetching a single revision returns just metadata by default
146
147 $ sendhttpv2peer << EOF
148 > command manifestdata
149 > nodes eval:[b'\x46\xa6\x72\x1b\x5e\xda\xf0\xea\x04\xb7\x9a\x5c\xb3\x21\x88\x54\xa4\xd2\xab\xa0']
150 > tree eval:b''
151 > EOF
152 creating http peer for wire protocol version 2
153 sending manifestdata command
154 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
155 s> Accept-Encoding: identity\r\n
156 s> accept: application/mercurial-exp-framing-0005\r\n
157 s> content-type: application/mercurial-exp-framing-0005\r\n
158 s> content-length: 67\r\n
159 s> host: $LOCALIP:$HGPORT\r\n (glob)
160 s> user-agent: Mercurial debugwireproto\r\n
161 s> \r\n
162 s> ;\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa2Enodes\x81TF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0Dtree@DnameLmanifestdata
163 s> makefile('rb', None)
164 s> HTTP/1.1 200 OK\r\n
165 s> Server: testing stub value\r\n
166 s> Date: $HTTP_DATE$\r\n
167 s> Content-Type: application/mercurial-exp-framing-0005\r\n
168 s> Transfer-Encoding: chunked\r\n
169 s> \r\n
170 s> 13\r\n
171 s> \x0b\x00\x00\x01\x00\x02\x011
172 s> \xa1FstatusBok
173 s> \r\n
174 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
175 s> 30\r\n
176 s> (\x00\x00\x01\x00\x02\x001
177 s> \xa1Jtotalitems\x01\xa1DnodeTF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0
178 s> \r\n
179 received frame(size=40; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
180 s> 8\r\n
181 s> \x00\x00\x00\x01\x00\x02\x002
182 s> \r\n
183 s> 0\r\n
184 s> \r\n
185 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
186 response: gen[
187 {
188 b'totalitems': 1
189 },
190 {
191 b'node': b'F\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0'
192 }
193 ]
194
195 Requesting parents works
196
197 $ sendhttpv2peer << EOF
198 > command manifestdata
199 > nodes eval:[b'\x46\xa6\x72\x1b\x5e\xda\xf0\xea\x04\xb7\x9a\x5c\xb3\x21\x88\x54\xa4\xd2\xab\xa0']
200 > tree eval:b''
201 > fields eval:[b'parents']
202 > EOF
203 creating http peer for wire protocol version 2
204 sending manifestdata command
205 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
206 s> Accept-Encoding: identity\r\n
207 s> accept: application/mercurial-exp-framing-0005\r\n
208 s> content-type: application/mercurial-exp-framing-0005\r\n
209 s> content-length: 83\r\n
210 s> host: $LOCALIP:$HGPORT\r\n (glob)
211 s> user-agent: Mercurial debugwireproto\r\n
212 s> \r\n
213 s> K\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81GparentsEnodes\x81TF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0Dtree@DnameLmanifestdata
214 s> makefile('rb', None)
215 s> HTTP/1.1 200 OK\r\n
216 s> Server: testing stub value\r\n
217 s> Date: $HTTP_DATE$\r\n
218 s> Content-Type: application/mercurial-exp-framing-0005\r\n
219 s> Transfer-Encoding: chunked\r\n
220 s> \r\n
221 s> 13\r\n
222 s> \x0b\x00\x00\x01\x00\x02\x011
223 s> \xa1FstatusBok
224 s> \r\n
225 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
226 s> 63\r\n
227 s> [\x00\x00\x01\x00\x02\x001
228 s> \xa1Jtotalitems\x01\xa2DnodeTF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0Gparents\x82T\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
229 s> \r\n
230 received frame(size=91; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
231 s> 8\r\n
232 s> \x00\x00\x00\x01\x00\x02\x002
233 s> \r\n
234 s> 0\r\n
235 s> \r\n
236 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
237 response: gen[
238 {
239 b'totalitems': 1
240 },
241 {
242 b'node': b'F\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0',
243 b'parents': [
244 b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
245 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
246 ]
247 }
248 ]
249
250 Requesting revision data works
251
252 $ sendhttpv2peer << EOF
253 > command manifestdata
254 > nodes eval:[b'\x46\xa6\x72\x1b\x5e\xda\xf0\xea\x04\xb7\x9a\x5c\xb3\x21\x88\x54\xa4\xd2\xab\xa0']
255 > tree eval:b''
256 > fields eval:[b'revision']
257 > EOF
258 creating http peer for wire protocol version 2
259 sending manifestdata command
260 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
261 s> Accept-Encoding: identity\r\n
262 s> accept: application/mercurial-exp-framing-0005\r\n
263 s> content-type: application/mercurial-exp-framing-0005\r\n
264 s> content-length: 84\r\n
265 s> host: $LOCALIP:$HGPORT\r\n (glob)
266 s> user-agent: Mercurial debugwireproto\r\n
267 s> \r\n
268 s> L\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81HrevisionEnodes\x81TF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0Dtree@DnameLmanifestdata
269 s> makefile('rb', None)
270 s> HTTP/1.1 200 OK\r\n
271 s> Server: testing stub value\r\n
272 s> Date: $HTTP_DATE$\r\n
273 s> Content-Type: application/mercurial-exp-framing-0005\r\n
274 s> Transfer-Encoding: chunked\r\n
275 s> \r\n
276 s> 13\r\n
277 s> \x0b\x00\x00\x01\x00\x02\x011
278 s> \xa1FstatusBok
279 s> \r\n
280 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
281 s> 98\r\n
282 s> \x90\x00\x00\x01\x00\x02\x001
283 s> \xa1Jtotalitems\x01\xa3MdeltabasenodeT\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Ideltasize\x187DnodeTF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0X7\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n
284 s> \r\n
285 received frame(size=144; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
286 s> 8\r\n
287 s> \x00\x00\x00\x01\x00\x02\x002
288 s> \r\n
289 s> 0\r\n
290 s> \r\n
291 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
292 response: gen[
293 {
294 b'totalitems': 1
295 },
296 {
297 b'deltabasenode': b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
298 b'deltasize': 55,
299 b'node': b'F\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0'
300 },
301 b'\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n'
302 ]
303
304 Requesting multiple revisions works
305
306 $ sendhttpv2peer << EOF
307 > command manifestdata
308 > nodes eval:[b'\x1b\x17\x5b\x59\x5f\x02\x2c\xfa\xb5\xb8\x09\xcc\x0e\xd5\x51\xbd\x0b\x3f\xf5\xe4', b'\x46\xa6\x72\x1b\x5e\xda\xf0\xea\x04\xb7\x9a\x5c\xb3\x21\x88\x54\xa4\xd2\xab\xa0']
309 > tree eval:b''
310 > fields eval:[b'revision']
311 > EOF
312 creating http peer for wire protocol version 2
313 sending manifestdata command
314 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
315 s> Accept-Encoding: identity\r\n
316 s> accept: application/mercurial-exp-framing-0005\r\n
317 s> content-type: application/mercurial-exp-framing-0005\r\n
318 s> content-length: 105\r\n
319 s> host: $LOCALIP:$HGPORT\r\n (glob)
320 s> user-agent: Mercurial debugwireproto\r\n
321 s> \r\n
322 s> a\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81HrevisionEnodes\x82T\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4TF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0Dtree@DnameLmanifestdata
323 s> makefile('rb', None)
324 s> HTTP/1.1 200 OK\r\n
325 s> Server: testing stub value\r\n
326 s> Date: $HTTP_DATE$\r\n
327 s> Content-Type: application/mercurial-exp-framing-0005\r\n
328 s> Transfer-Encoding: chunked\r\n
329 s> \r\n
330 s> 13\r\n
331 s> \x0b\x00\x00\x01\x00\x02\x011
332 s> \xa1FstatusBok
333 s> \r\n
334 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
335 s> 1ea\r\n
336 s> \xe2\x01\x00\x01\x00\x02\x001
337 s> \xa1Jtotalitems\x02\xa2DnodeT\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Lrevisionsize\x19\x01$Y\x01$a\x002b4eb07319bfa077a40a2f04913659aef0da42da\n
338 s> b\x00819e258d31a5e1606629f365bb902a1b21ee4216\n
339 s> dir0/c\x00914445346a0ca0629bd47ceb5dfe07e4d4cf2501\n
340 s> dir0/child0/e\x00bbba6c06b30f443d34ff841bc985c4d0827c6be4\n
341 s> dir0/child1/f\x0012fc7dcd773b5a0a929ce195228083c6ddc9cec4\n
342 s> dir0/d\x00538206dc971e521540d6843abfe6d16032f6d426\n
343 s> \xa3MdeltabasenodeT\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Ideltasize\x187DnodeTF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0X7\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n
344 s> \r\n
345 received frame(size=482; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
346 s> 8\r\n
347 s> \x00\x00\x00\x01\x00\x02\x002
348 s> \r\n
349 s> 0\r\n
350 s> \r\n
351 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
352 response: gen[
353 {
354 b'totalitems': 2
355 },
356 {
357 b'node': b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
358 b'revisionsize': 292
359 },
360 b'a\x002b4eb07319bfa077a40a2f04913659aef0da42da\nb\x00819e258d31a5e1606629f365bb902a1b21ee4216\ndir0/c\x00914445346a0ca0629bd47ceb5dfe07e4d4cf2501\ndir0/child0/e\x00bbba6c06b30f443d34ff841bc985c4d0827c6be4\ndir0/child1/f\x0012fc7dcd773b5a0a929ce195228083c6ddc9cec4\ndir0/d\x00538206dc971e521540d6843abfe6d16032f6d426\n',
361 {
362 b'deltabasenode': b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
363 b'deltasize': 55,
364 b'node': b'F\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0'
365 },
366 b'\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n'
367 ]
368
369 Revisions are sorted by DAG order, parents first
370
371 $ sendhttpv2peer << EOF
372 > command manifestdata
373 > nodes eval:[b'\x46\xa6\x72\x1b\x5e\xda\xf0\xea\x04\xb7\x9a\x5c\xb3\x21\x88\x54\xa4\xd2\xab\xa0', b'\x1b\x17\x5b\x59\x5f\x02\x2c\xfa\xb5\xb8\x09\xcc\x0e\xd5\x51\xbd\x0b\x3f\xf5\xe4']
374 > tree eval:b''
375 > fields eval:[b'revision']
376 > EOF
377 creating http peer for wire protocol version 2
378 sending manifestdata command
379 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
380 s> Accept-Encoding: identity\r\n
381 s> accept: application/mercurial-exp-framing-0005\r\n
382 s> content-type: application/mercurial-exp-framing-0005\r\n
383 s> content-length: 105\r\n
384 s> host: $LOCALIP:$HGPORT\r\n (glob)
385 s> user-agent: Mercurial debugwireproto\r\n
386 s> \r\n
387 s> a\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81HrevisionEnodes\x82TF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0T\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Dtree@DnameLmanifestdata
388 s> makefile('rb', None)
389 s> HTTP/1.1 200 OK\r\n
390 s> Server: testing stub value\r\n
391 s> Date: $HTTP_DATE$\r\n
392 s> Content-Type: application/mercurial-exp-framing-0005\r\n
393 s> Transfer-Encoding: chunked\r\n
394 s> \r\n
395 s> 13\r\n
396 s> \x0b\x00\x00\x01\x00\x02\x011
397 s> \xa1FstatusBok
398 s> \r\n
399 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
400 s> 1ea\r\n
401 s> \xe2\x01\x00\x01\x00\x02\x001
402 s> \xa1Jtotalitems\x02\xa2DnodeT\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Lrevisionsize\x19\x01$Y\x01$a\x002b4eb07319bfa077a40a2f04913659aef0da42da\n
403 s> b\x00819e258d31a5e1606629f365bb902a1b21ee4216\n
404 s> dir0/c\x00914445346a0ca0629bd47ceb5dfe07e4d4cf2501\n
405 s> dir0/child0/e\x00bbba6c06b30f443d34ff841bc985c4d0827c6be4\n
406 s> dir0/child1/f\x0012fc7dcd773b5a0a929ce195228083c6ddc9cec4\n
407 s> dir0/d\x00538206dc971e521540d6843abfe6d16032f6d426\n
408 s> \xa3MdeltabasenodeT\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Ideltasize\x187DnodeTF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0X7\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n
409 s> \r\n
410 received frame(size=482; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
411 s> 8\r\n
412 s> \x00\x00\x00\x01\x00\x02\x002
413 s> \r\n
414 s> 0\r\n
415 s> \r\n
416 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
417 response: gen[
418 {
419 b'totalitems': 2
420 },
421 {
422 b'node': b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
423 b'revisionsize': 292
424 },
425 b'a\x002b4eb07319bfa077a40a2f04913659aef0da42da\nb\x00819e258d31a5e1606629f365bb902a1b21ee4216\ndir0/c\x00914445346a0ca0629bd47ceb5dfe07e4d4cf2501\ndir0/child0/e\x00bbba6c06b30f443d34ff841bc985c4d0827c6be4\ndir0/child1/f\x0012fc7dcd773b5a0a929ce195228083c6ddc9cec4\ndir0/d\x00538206dc971e521540d6843abfe6d16032f6d426\n',
426 {
427 b'deltabasenode': b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
428 b'deltasize': 55,
429 b'node': b'F\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0'
430 },
431 b'\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n'
432 ]
433
434 Requesting parents and revision data works
435
436 $ sendhttpv2peer << EOF
437 > command manifestdata
438 > nodes eval:[b'\x1b\x17\x5b\x59\x5f\x02\x2c\xfa\xb5\xb8\x09\xcc\x0e\xd5\x51\xbd\x0b\x3f\xf5\xe4', b'\x46\xa6\x72\x1b\x5e\xda\xf0\xea\x04\xb7\x9a\x5c\xb3\x21\x88\x54\xa4\xd2\xab\xa0']
439 > tree eval:b''
440 > fields eval:[b'parents', b'revision']
441 > EOF
442 creating http peer for wire protocol version 2
443 sending manifestdata command
444 s> POST /api/exp-http-v2-0001/ro/manifestdata HTTP/1.1\r\n
445 s> Accept-Encoding: identity\r\n
446 s> accept: application/mercurial-exp-framing-0005\r\n
447 s> content-type: application/mercurial-exp-framing-0005\r\n
448 s> content-length: 113\r\n
449 s> host: $LOCALIP:$HGPORT\r\n (glob)
450 s> user-agent: Mercurial debugwireproto\r\n
451 s> \r\n
452 s> i\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x82GparentsHrevisionEnodes\x82T\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4TF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0Dtree@DnameLmanifestdata
453 s> makefile('rb', None)
454 s> HTTP/1.1 200 OK\r\n
455 s> Server: testing stub value\r\n
456 s> Date: $HTTP_DATE$\r\n
457 s> Content-Type: application/mercurial-exp-framing-0005\r\n
458 s> Transfer-Encoding: chunked\r\n
459 s> \r\n
460 s> 13\r\n
461 s> \x0b\x00\x00\x01\x00\x02\x011
462 s> \xa1FstatusBok
463 s> \r\n
464 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
465 s> 250\r\n
466 s> H\x02\x00\x01\x00\x02\x001
467 s> \xa1Jtotalitems\x02\xa3DnodeT\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Gparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Lrevisionsize\x19\x01$Y\x01$a\x002b4eb07319bfa077a40a2f04913659aef0da42da\n
468 s> b\x00819e258d31a5e1606629f365bb902a1b21ee4216\n
469 s> dir0/c\x00914445346a0ca0629bd47ceb5dfe07e4d4cf2501\n
470 s> dir0/child0/e\x00bbba6c06b30f443d34ff841bc985c4d0827c6be4\n
471 s> dir0/child1/f\x0012fc7dcd773b5a0a929ce195228083c6ddc9cec4\n
472 s> dir0/d\x00538206dc971e521540d6843abfe6d16032f6d426\n
473 s> \xa4MdeltabasenodeT\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4Ideltasize\x187DnodeTF\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0Gparents\x82T\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X7\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n
474 s> \r\n
475 received frame(size=584; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
476 s> 8\r\n
477 s> \x00\x00\x00\x01\x00\x02\x002
478 s> \r\n
479 s> 0\r\n
480 s> \r\n
481 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
482 response: gen[
483 {
484 b'totalitems': 2
485 },
486 {
487 b'node': b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
488 b'parents': [
489 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
490 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
491 ],
492 b'revisionsize': 292
493 },
494 b'a\x002b4eb07319bfa077a40a2f04913659aef0da42da\nb\x00819e258d31a5e1606629f365bb902a1b21ee4216\ndir0/c\x00914445346a0ca0629bd47ceb5dfe07e4d4cf2501\ndir0/child0/e\x00bbba6c06b30f443d34ff841bc985c4d0827c6be4\ndir0/child1/f\x0012fc7dcd773b5a0a929ce195228083c6ddc9cec4\ndir0/d\x00538206dc971e521540d6843abfe6d16032f6d426\n',
495 {
496 b'deltabasenode': b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
497 b'deltasize': 55,
498 b'node': b'F\xa6r\x1b^\xda\xf0\xea\x04\xb7\x9a\\\xb3!\x88T\xa4\xd2\xab\xa0',
499 b'parents': [
500 b'\x1b\x17[Y_\x02,\xfa\xb5\xb8\t\xcc\x0e\xd5Q\xbd\x0b?\xf5\xe4',
501 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
502 ]
503 },
504 b'\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00+a\x000879345e39377229634b420c639454156726c6b6\n'
505 ]
506
507 $ cat error.log
@@ -1,278 +1,352 b''
1 1 **Experimental and under active development**
2 2
3 3 This section documents the wire protocol commands exposed to transports
4 4 using the frame-based protocol. The set of commands exposed through
5 5 these transports is distinct from the set of commands exposed to legacy
6 6 transports.
7 7
8 8 The frame-based protocol uses CBOR to encode command execution requests.
9 9 All command arguments must be mapped to a specific or set of CBOR data
10 10 types.
11 11
12 12 The response to many commands is also CBOR. There is no common response
13 13 format: each command defines its own response format.
14 14
15 15 TODOs
16 16 =====
17 17
18 18 * Add "node namespace" support to each command. In order to support
19 19 SHA-1 hash transition, we want servers to be able to expose different
20 20 "node namespaces" for the same data. Every command operating on nodes
21 21 should specify which "node namespace" it is operating on and responses
22 22 should encode the "node namespace" accordingly.
23 23
24 24 Commands
25 25 ========
26 26
27 27 The sections below detail all commands available to wire protocol version
28 28 2.
29 29
30 30 branchmap
31 31 ---------
32 32
33 33 Obtain heads in named branches.
34 34
35 35 Receives no arguments.
36 36
37 37 The response is a map with bytestring keys defining the branch name.
38 38 Values are arrays of bytestring defining raw changeset nodes.
39 39
40 40 capabilities
41 41 ------------
42 42
43 43 Obtain the server's capabilities.
44 44
45 45 Receives no arguments.
46 46
47 47 This command is typically called only as part of the handshake during
48 48 initial connection establishment.
49 49
50 50 The response is a map with bytestring keys defining server information.
51 51
52 52 The defined keys are:
53 53
54 54 commands
55 55 A map defining available wire protocol commands on this server.
56 56
57 57 Keys in the map are the names of commands that can be invoked. Values
58 58 are maps defining information about that command. The bytestring keys
59 59 are:
60 60
61 61 args
62 62 A map of argument names and their expected types.
63 63
64 64 Types are defined as a representative value for the expected type.
65 65 e.g. an argument expecting a boolean type will have its value
66 66 set to true. An integer type will have its value set to 42. The
67 67 actual values are arbitrary and may not have meaning.
68 68 permissions
69 69 An array of permissions required to execute this command.
70 70
71 71 compression
72 72 An array of maps defining available compression format support.
73 73
74 74 The array is sorted from most preferred to least preferred.
75 75
76 76 Each entry has the following bytestring keys:
77 77
78 78 name
79 79 Name of the compression engine. e.g. ``zstd`` or ``zlib``.
80 80
81 81 framingmediatypes
82 82 An array of bytestrings defining the supported framing protocol
83 83 media types. Servers will not accept media types not in this list.
84 84
85 85 rawrepoformats
86 86 An array of storage formats the repository is using. This set of
87 87 requirements can be used to determine whether a client can read a
88 88 *raw* copy of file data available.
89 89
90 90 changesetdata
91 91 -------------
92 92
93 93 Obtain various data related to changesets.
94 94
95 95 The command accepts the following arguments:
96 96
97 97 noderange
98 98 (array of arrays of bytestrings) An array of 2 elements, each being an
99 99 array of node bytestrings. The first array denotes the changelog revisions
100 100 that are already known to the client. The second array denotes the changelog
101 101 revision DAG heads to fetch. The argument essentially defines a DAG range
102 102 bounded by root and head nodes to fetch.
103 103
104 104 The roots array may be empty. The heads array must be defined.
105 105
106 106 nodes
107 107 (array of bytestrings) Changelog revisions to request explicitly.
108 108
109 109 fields
110 110 (set of bytestring) Which data associated with changelog revisions to
111 111 fetch. The following values are recognized:
112 112
113 113 bookmarks
114 114 Bookmarks associated with a revision.
115 115
116 116 parents
117 117 Parent revisions.
118 118
119 119 phase
120 120 The phase state of a revision.
121 121
122 122 revision
123 123 The raw, revision data for the changelog entry. The hash of this data
124 124 will match the revision's node value.
125 125
126 126 The server resolves the set of revisions relevant to the request by taking
127 127 the union of the ``noderange`` and ``nodes`` arguments. At least one of these
128 128 arguments must be defined.
129 129
130 130 The response bytestream starts with a CBOR map describing the data that follows.
131 131 This map has the following bytestring keys:
132 132
133 133 totalitems
134 134 (unsigned integer) Total number of changelog revisions whose data is being
135 135 transferred. This maps to the set of revisions in the requested node
136 136 range, not the total number of records that follow (see below for why).
137 137
138 138 Following the map header is a series of 0 or more CBOR values. If values
139 139 are present, the first value will always be a map describing a single changeset
140 140 revision. If revision data is requested, the raw revision data (encoded as
141 141 a CBOR bytestring) will follow the map describing it. Otherwise, another CBOR
142 142 map describing the next changeset revision will occur.
143 143
144 144 Each map has the following bytestring keys:
145 145
146 146 node
147 147 (bytestring) The node value for this revision. This is the SHA-1 hash of
148 148 the raw revision data.
149 149
150 150 bookmarks (optional)
151 151 (array of bytestrings) Bookmarks attached to this revision. Only present
152 152 if ``bookmarks`` data is being requested and the revision has bookmarks
153 153 attached.
154 154
155 155 parents (optional)
156 156 (array of bytestrings) The nodes representing the parent revisions of this
157 157 revision. Only present if ``parents`` data is being requested.
158 158
159 159 phase (optional)
160 160 (bytestring) The phase that a revision is in. Recognized values are
161 161 ``secret``, ``draft``, and ``public``. Only present if ``phase`` data
162 162 is being requested.
163 163
164 164 revisionsize (optional)
165 165 (unsigned integer) Indicates the size of raw revision data that follows this
166 166 map. The following data contains a serialized form of the changeset data,
167 167 including the author, date, commit message, set of changed files, manifest
168 168 node, and other metadata.
169 169
170 170 Only present if ``revision`` data was requested and the data follows this
171 171 map.
172 172
173 173 If nodes are requested via ``noderange``, they will be emitted in DAG order,
174 174 parents always before children.
175 175
176 176 If nodes are requested via ``nodes``, they will be emitted in requested order.
177 177
178 178 Nodes from ``nodes`` are emitted before nodes from ``noderange``.
179 179
180 180 The set of changeset revisions emitted may not match the exact set of
181 181 changesets requested. Furthermore, the set of keys present on each
182 182 map may vary. This is to facilitate emitting changeset updates as well
183 183 as new revisions.
184 184
185 185 For example, if the request wants ``phase`` and ``revision`` data,
186 186 the response may contain entries for each changeset in the common nodes
187 187 set with the ``phase`` key and without the ``revision`` key in order
188 188 to reflect a phase-only update.
189 189
190 190 TODO support different revision selection mechanisms (e.g. non-public, specific
191 191 revisions)
192 192 TODO support different hash "namespaces" for revisions (e.g. sha-1 versus other)
193 193 TODO support emitting obsolescence data
194 194 TODO support filtering based on relevant paths (narrow clone)
195 195 TODO support depth limiting
196 196 TODO support hgtagsfnodes cache / tags data
197 197 TODO support branch heads cache
198 198
199 199 heads
200 200 -----
201 201
202 202 Obtain DAG heads in the repository.
203 203
204 204 The command accepts the following arguments:
205 205
206 206 publiconly (optional)
207 207 (boolean) If set, operate on the DAG for public phase changesets only.
208 208 Non-public (i.e. draft) phase DAG heads will not be returned.
209 209
210 210 The response is a CBOR array of bytestrings defining changeset nodes
211 211 of DAG heads. The array can be empty if the repository is empty or no
212 212 changesets satisfied the request.
213 213
214 214 TODO consider exposing phase of heads in response
215 215
216 216 known
217 217 -----
218 218
219 219 Determine whether a series of changeset nodes is known to the server.
220 220
221 221 The command accepts the following arguments:
222 222
223 223 nodes
224 224 (array of bytestrings) List of changeset nodes whose presence to
225 225 query.
226 226
227 227 The response is a bytestring where each byte contains a 0 or 1 for the
228 228 corresponding requested node at the same index.
229 229
230 230 TODO use a bit array for even more compact response
231 231
232 232 listkeys
233 233 --------
234 234
235 235 List values in a specified ``pushkey`` namespace.
236 236
237 237 The command receives the following arguments:
238 238
239 239 namespace
240 240 (bytestring) Pushkey namespace to query.
241 241
242 242 The response is a map with bytestring keys and values.
243 243
244 244 TODO consider using binary to represent nodes in certain pushkey namespaces.
245 245
246 246 lookup
247 247 ------
248 248
249 249 Try to resolve a value to a changeset revision.
250 250
251 251 Unlike ``known`` which operates on changeset nodes, lookup operates on
252 252 node fragments and other names that a user may use.
253 253
254 254 The command receives the following arguments:
255 255
256 256 key
257 257 (bytestring) Value to try to resolve.
258 258
259 259 On success, returns a bytestring containing the resolved node.
260 260
261 manifestdata
262 ------------
263
264 Obtain various data related to manifests (which are lists of files in
265 a revision).
266
267 The command accepts the following arguments:
268
269 fields
270 (set of bytestring) Which data associated with manifests to fetch.
271 The following values are recognized:
272
273 parents
274 Parent nodes for the manifest.
275
276 revision
277 The raw revision data for the manifest.
278
279 nodes
280 (array of bytestring) Manifest nodes whose data to retrieve.
281
282 tree
283 (bytestring) Path to manifest to retrieve. The empty bytestring represents
284 the root manifest. All other values represent directories/trees within
285 the repository.
286
287 TODO allow specifying revisions via alternate means (such as from changeset
288 revisions or ranges)
289 TODO consider recursive expansion of manifests (with path filtering for
290 narrow use cases)
291 TODO more control over whether to emit fulltexts or deltas
292
293 The response bytestream starts with a CBOR map describing the data that
294 follows. It has the following bytestring keys:
295
296 totalitems
297 (unsigned integer) Total number of manifest revisions whose data is
298 being returned.
299
300 Following the header map is a series of 0 or more CBOR values. The first
301 value is always a map describing a manifest revision. If this map has the
302 ``deltasize`` or ``revisionsize`` keys, a bytestring containing the delta
303 or revision, respectively, will immediately follow the map. Otherwise
304 the next value will be a map describing the next manifest revision.
305
306 Each map has the following bytestring keys:
307
308 node
309 (bytestring) The node of the manifest revision whose data is represented.
310
311 deltabasenode
312 (bytestring) The node that the delta representation of this revision is
313 computed against. Only present if the ``revision`` field is requested and
314 a delta is being emitted.
315
316 deltasize
317 (unsigned integer) The size of the delta data that follows this map.
318 Only present if the ``revision`` field is requested and a delta is
319 being emitted.
320
321 parents
322 (array of bytestring) The nodes of the parents of this manifest revision.
323 Only present if the ``parents`` field is requested.
324
325 revisionsize
326 (unsigned integer) The size of the fulltext revision data that follows
327 this map. Only present if the ``revision`` field is requested and a fulltext
328 revision is being emitted.
329
330 When ``revision`` data is requested, the server chooses to emit either fulltext
331 revision data or a delta. What the server decides can be inferred by looking
332 for the presence of the ``deltasize`` or ``revisionsize`` keys in the map.
333 Servers MUST NOT define both keys.
334
261 335 pushkey
262 336 -------
263 337
264 338 Set a value using the ``pushkey`` protocol.
265 339
266 340 The command receives the following arguments:
267 341
268 342 namespace
269 343 (bytestring) Pushkey namespace to operate on.
270 344 key
271 345 (bytestring) The pushkey key to set.
272 346 old
273 347 (bytestring) Old value for this key.
274 348 new
275 349 (bytestring) New value for this key.
276 350
277 351 TODO consider using binary to represent nodes is certain pushkey namespaces.
278 352 TODO better define response type and meaning.
@@ -1,646 +1,791 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 12 from .node import (
13 13 nullid,
14 nullrev,
14 15 )
15 16 from . import (
17 changegroup,
18 dagop,
16 19 discovery,
17 20 encoding,
18 21 error,
19 22 pycompat,
20 23 streamclone,
21 24 util,
22 25 wireprotoframing,
23 26 wireprototypes,
24 27 )
25 28 from .utils import (
26 29 interfaceutil,
27 30 )
28 31
29 32 FRAMINGTYPE = b'application/mercurial-exp-framing-0005'
30 33
31 34 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
32 35
33 36 COMMANDS = wireprototypes.commanddict()
34 37
35 38 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
36 39 from .hgweb import common as hgwebcommon
37 40
38 41 # URL space looks like: <permissions>/<command>, where <permission> can
39 42 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
40 43
41 44 # Root URL does nothing meaningful... yet.
42 45 if not urlparts:
43 46 res.status = b'200 OK'
44 47 res.headers[b'Content-Type'] = b'text/plain'
45 48 res.setbodybytes(_('HTTP version 2 API handler'))
46 49 return
47 50
48 51 if len(urlparts) == 1:
49 52 res.status = b'404 Not Found'
50 53 res.headers[b'Content-Type'] = b'text/plain'
51 54 res.setbodybytes(_('do not know how to process %s\n') %
52 55 req.dispatchpath)
53 56 return
54 57
55 58 permission, command = urlparts[0:2]
56 59
57 60 if permission not in (b'ro', b'rw'):
58 61 res.status = b'404 Not Found'
59 62 res.headers[b'Content-Type'] = b'text/plain'
60 63 res.setbodybytes(_('unknown permission: %s') % permission)
61 64 return
62 65
63 66 if req.method != 'POST':
64 67 res.status = b'405 Method Not Allowed'
65 68 res.headers[b'Allow'] = b'POST'
66 69 res.setbodybytes(_('commands require POST requests'))
67 70 return
68 71
69 72 # At some point we'll want to use our own API instead of recycling the
70 73 # behavior of version 1 of the wire protocol...
71 74 # TODO return reasonable responses - not responses that overload the
72 75 # HTTP status line message for error reporting.
73 76 try:
74 77 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
75 78 except hgwebcommon.ErrorResponse as e:
76 79 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
77 80 for k, v in e.headers:
78 81 res.headers[k] = v
79 82 res.setbodybytes('permission denied')
80 83 return
81 84
82 85 # We have a special endpoint to reflect the request back at the client.
83 86 if command == b'debugreflect':
84 87 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
85 88 return
86 89
87 90 # Extra commands that we handle that aren't really wire protocol
88 91 # commands. Think extra hard before making this hackery available to
89 92 # extension.
90 93 extracommands = {'multirequest'}
91 94
92 95 if command not in COMMANDS and command not in extracommands:
93 96 res.status = b'404 Not Found'
94 97 res.headers[b'Content-Type'] = b'text/plain'
95 98 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
96 99 return
97 100
98 101 repo = rctx.repo
99 102 ui = repo.ui
100 103
101 104 proto = httpv2protocolhandler(req, ui)
102 105
103 106 if (not COMMANDS.commandavailable(command, proto)
104 107 and command not in extracommands):
105 108 res.status = b'404 Not Found'
106 109 res.headers[b'Content-Type'] = b'text/plain'
107 110 res.setbodybytes(_('invalid wire protocol command: %s') % command)
108 111 return
109 112
110 113 # TODO consider cases where proxies may add additional Accept headers.
111 114 if req.headers.get(b'Accept') != FRAMINGTYPE:
112 115 res.status = b'406 Not Acceptable'
113 116 res.headers[b'Content-Type'] = b'text/plain'
114 117 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
115 118 % FRAMINGTYPE)
116 119 return
117 120
118 121 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
119 122 res.status = b'415 Unsupported Media Type'
120 123 # TODO we should send a response with appropriate media type,
121 124 # since client does Accept it.
122 125 res.headers[b'Content-Type'] = b'text/plain'
123 126 res.setbodybytes(_('client MUST send Content-Type header with '
124 127 'value: %s\n') % FRAMINGTYPE)
125 128 return
126 129
127 130 _processhttpv2request(ui, repo, req, res, permission, command, proto)
128 131
129 132 def _processhttpv2reflectrequest(ui, repo, req, res):
130 133 """Reads unified frame protocol request and dumps out state to client.
131 134
132 135 This special endpoint can be used to help debug the wire protocol.
133 136
134 137 Instead of routing the request through the normal dispatch mechanism,
135 138 we instead read all frames, decode them, and feed them into our state
136 139 tracker. We then dump the log of all that activity back out to the
137 140 client.
138 141 """
139 142 import json
140 143
141 144 # Reflection APIs have a history of being abused, accidentally disclosing
142 145 # sensitive data, etc. So we have a config knob.
143 146 if not ui.configbool('experimental', 'web.api.debugreflect'):
144 147 res.status = b'404 Not Found'
145 148 res.headers[b'Content-Type'] = b'text/plain'
146 149 res.setbodybytes(_('debugreflect service not available'))
147 150 return
148 151
149 152 # We assume we have a unified framing protocol request body.
150 153
151 154 reactor = wireprotoframing.serverreactor()
152 155 states = []
153 156
154 157 while True:
155 158 frame = wireprotoframing.readframe(req.bodyfh)
156 159
157 160 if not frame:
158 161 states.append(b'received: <no frame>')
159 162 break
160 163
161 164 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
162 165 frame.requestid,
163 166 frame.payload))
164 167
165 168 action, meta = reactor.onframerecv(frame)
166 169 states.append(json.dumps((action, meta), sort_keys=True,
167 170 separators=(', ', ': ')))
168 171
169 172 action, meta = reactor.oninputeof()
170 173 meta['action'] = action
171 174 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
172 175
173 176 res.status = b'200 OK'
174 177 res.headers[b'Content-Type'] = b'text/plain'
175 178 res.setbodybytes(b'\n'.join(states))
176 179
177 180 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
178 181 """Post-validation handler for HTTPv2 requests.
179 182
180 183 Called when the HTTP request contains unified frame-based protocol
181 184 frames for evaluation.
182 185 """
183 186 # TODO Some HTTP clients are full duplex and can receive data before
184 187 # the entire request is transmitted. Figure out a way to indicate support
185 188 # for that so we can opt into full duplex mode.
186 189 reactor = wireprotoframing.serverreactor(deferoutput=True)
187 190 seencommand = False
188 191
189 192 outstream = reactor.makeoutputstream()
190 193
191 194 while True:
192 195 frame = wireprotoframing.readframe(req.bodyfh)
193 196 if not frame:
194 197 break
195 198
196 199 action, meta = reactor.onframerecv(frame)
197 200
198 201 if action == 'wantframe':
199 202 # Need more data before we can do anything.
200 203 continue
201 204 elif action == 'runcommand':
202 205 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
203 206 reqcommand, reactor, outstream,
204 207 meta, issubsequent=seencommand)
205 208
206 209 if sentoutput:
207 210 return
208 211
209 212 seencommand = True
210 213
211 214 elif action == 'error':
212 215 # TODO define proper error mechanism.
213 216 res.status = b'200 OK'
214 217 res.headers[b'Content-Type'] = b'text/plain'
215 218 res.setbodybytes(meta['message'] + b'\n')
216 219 return
217 220 else:
218 221 raise error.ProgrammingError(
219 222 'unhandled action from frame processor: %s' % action)
220 223
221 224 action, meta = reactor.oninputeof()
222 225 if action == 'sendframes':
223 226 # We assume we haven't started sending the response yet. If we're
224 227 # wrong, the response type will raise an exception.
225 228 res.status = b'200 OK'
226 229 res.headers[b'Content-Type'] = FRAMINGTYPE
227 230 res.setbodygen(meta['framegen'])
228 231 elif action == 'noop':
229 232 pass
230 233 else:
231 234 raise error.ProgrammingError('unhandled action from frame processor: %s'
232 235 % action)
233 236
234 237 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
235 238 outstream, command, issubsequent):
236 239 """Dispatch a wire protocol command made from HTTPv2 requests.
237 240
238 241 The authenticated permission (``authedperm``) along with the original
239 242 command from the URL (``reqcommand``) are passed in.
240 243 """
241 244 # We already validated that the session has permissions to perform the
242 245 # actions in ``authedperm``. In the unified frame protocol, the canonical
243 246 # command to run is expressed in a frame. However, the URL also requested
244 247 # to run a specific command. We need to be careful that the command we
245 248 # run doesn't have permissions requirements greater than what was granted
246 249 # by ``authedperm``.
247 250 #
248 251 # Our rule for this is we only allow one command per HTTP request and
249 252 # that command must match the command in the URL. However, we make
250 253 # an exception for the ``multirequest`` URL. This URL is allowed to
251 254 # execute multiple commands. We double check permissions of each command
252 255 # as it is invoked to ensure there is no privilege escalation.
253 256 # TODO consider allowing multiple commands to regular command URLs
254 257 # iff each command is the same.
255 258
256 259 proto = httpv2protocolhandler(req, ui, args=command['args'])
257 260
258 261 if reqcommand == b'multirequest':
259 262 if not COMMANDS.commandavailable(command['command'], proto):
260 263 # TODO proper error mechanism
261 264 res.status = b'200 OK'
262 265 res.headers[b'Content-Type'] = b'text/plain'
263 266 res.setbodybytes(_('wire protocol command not available: %s') %
264 267 command['command'])
265 268 return True
266 269
267 270 # TODO don't use assert here, since it may be elided by -O.
268 271 assert authedperm in (b'ro', b'rw')
269 272 wirecommand = COMMANDS[command['command']]
270 273 assert wirecommand.permission in ('push', 'pull')
271 274
272 275 if authedperm == b'ro' and wirecommand.permission != 'pull':
273 276 # TODO proper error mechanism
274 277 res.status = b'403 Forbidden'
275 278 res.headers[b'Content-Type'] = b'text/plain'
276 279 res.setbodybytes(_('insufficient permissions to execute '
277 280 'command: %s') % command['command'])
278 281 return True
279 282
280 283 # TODO should we also call checkperm() here? Maybe not if we're going
281 284 # to overhaul that API. The granted scope from the URL check should
282 285 # be good enough.
283 286
284 287 else:
285 288 # Don't allow multiple commands outside of ``multirequest`` URL.
286 289 if issubsequent:
287 290 # TODO proper error mechanism
288 291 res.status = b'200 OK'
289 292 res.headers[b'Content-Type'] = b'text/plain'
290 293 res.setbodybytes(_('multiple commands cannot be issued to this '
291 294 'URL'))
292 295 return True
293 296
294 297 if reqcommand != command['command']:
295 298 # TODO define proper error mechanism
296 299 res.status = b'200 OK'
297 300 res.headers[b'Content-Type'] = b'text/plain'
298 301 res.setbodybytes(_('command in frame must match command in URL'))
299 302 return True
300 303
301 304 res.status = b'200 OK'
302 305 res.headers[b'Content-Type'] = FRAMINGTYPE
303 306
304 307 try:
305 308 objs = dispatch(repo, proto, command['command'])
306 309
307 310 action, meta = reactor.oncommandresponsereadyobjects(
308 311 outstream, command['requestid'], objs)
309 312
310 313 except Exception as e:
311 314 action, meta = reactor.onservererror(
312 315 outstream, command['requestid'],
313 316 _('exception when invoking command: %s') % e)
314 317
315 318 if action == 'sendframes':
316 319 res.setbodygen(meta['framegen'])
317 320 return True
318 321 elif action == 'noop':
319 322 return False
320 323 else:
321 324 raise error.ProgrammingError('unhandled event from reactor: %s' %
322 325 action)
323 326
324 327 def getdispatchrepo(repo, proto, command):
325 328 return repo.filtered('served')
326 329
327 330 def dispatch(repo, proto, command):
328 331 repo = getdispatchrepo(repo, proto, command)
329 332
330 333 func, spec = COMMANDS[command]
331 334 args = proto.getargs(spec)
332 335
333 336 return func(repo, proto, **args)
334 337
335 338 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
336 339 class httpv2protocolhandler(object):
337 340 def __init__(self, req, ui, args=None):
338 341 self._req = req
339 342 self._ui = ui
340 343 self._args = args
341 344
342 345 @property
343 346 def name(self):
344 347 return HTTP_WIREPROTO_V2
345 348
346 349 def getargs(self, args):
347 350 data = {}
348 351 for k, typ in args.items():
349 352 if k == '*':
350 353 raise NotImplementedError('do not support * args')
351 354 elif k in self._args:
352 355 # TODO consider validating value types.
353 356 data[k] = self._args[k]
354 357
355 358 return data
356 359
357 360 def getprotocaps(self):
358 361 # Protocol capabilities are currently not implemented for HTTP V2.
359 362 return set()
360 363
361 364 def getpayload(self):
362 365 raise NotImplementedError
363 366
364 367 @contextlib.contextmanager
365 368 def mayberedirectstdio(self):
366 369 raise NotImplementedError
367 370
368 371 def client(self):
369 372 raise NotImplementedError
370 373
371 374 def addcapabilities(self, repo, caps):
372 375 return caps
373 376
374 377 def checkperm(self, perm):
375 378 raise NotImplementedError
376 379
377 380 def httpv2apidescriptor(req, repo):
378 381 proto = httpv2protocolhandler(req, repo.ui)
379 382
380 383 return _capabilitiesv2(repo, proto)
381 384
382 385 def _capabilitiesv2(repo, proto):
383 386 """Obtain the set of capabilities for version 2 transports.
384 387
385 388 These capabilities are distinct from the capabilities for version 1
386 389 transports.
387 390 """
388 391 compression = []
389 392 for engine in wireprototypes.supportedcompengines(repo.ui, util.SERVERROLE):
390 393 compression.append({
391 394 b'name': engine.wireprotosupport().name,
392 395 })
393 396
394 397 caps = {
395 398 'commands': {},
396 399 'compression': compression,
397 400 'framingmediatypes': [FRAMINGTYPE],
398 401 }
399 402
400 403 # TODO expose available changesetdata fields.
401 404
402 405 for command, entry in COMMANDS.items():
403 406 caps['commands'][command] = {
404 407 'args': entry.args,
405 408 'permissions': [entry.permission],
406 409 }
407 410
408 411 if streamclone.allowservergeneration(repo):
409 412 caps['rawrepoformats'] = sorted(repo.requirements &
410 413 repo.supportedformats)
411 414
412 415 return proto.addcapabilities(repo, caps)
413 416
417 def builddeltarequests(store, nodes):
418 """Build a series of revision delta requests against a backend store.
419
420 Returns a list of revision numbers in the order they should be sent
421 and a list of ``irevisiondeltarequest`` instances to be made against
422 the backend store.
423 """
424 # We sort and send nodes in DAG order because this is optimal for
425 # storage emission.
426 # TODO we may want a better storage API here - one where we can throw
427 # a list of nodes and delta preconditions over a figurative wall and
428 # have the storage backend figure it out for us.
429 revs = dagop.linearize({store.rev(n) for n in nodes}, store.parentrevs)
430
431 requests = []
432
433 for rev in revs:
434 node = store.node(rev)
435 parents = store.parents(node)
436 deltaparent = store.node(store.deltaparent(rev))
437
438 # There is a delta in storage. That means we can send the delta
439 # efficiently.
440 #
441 # But, the delta may be against a revision the receiver doesn't
442 # have (e.g. shallow clone or when the delta isn't against a parent
443 # revision). For now, we ignore the problem of shallow clone. As
444 # long as a delta exists against a parent, we send it.
445 # TODO allow arguments to control this behavior, as the receiver
446 # may not have the base revision in some scenarios.
447 if deltaparent != nullid and deltaparent in parents:
448 basenode = deltaparent
449
450 # Else there is no delta parent in storage or the delta that is
451 # # there isn't suitable. Let's use a delta against a parent
452 # revision, if possible.
453 #
454 # There is room to check if the delta parent is in the ancestry of
455 # this node. But there isn't an API on the manifest storage object
456 # for that. So ignore this case for now.
457
458 elif parents[0] != nullid:
459 basenode = parents[0]
460 elif parents[1] != nullid:
461 basenode = parents[1]
462
463 # No potential bases to delta against. Send a full revision.
464 else:
465 basenode = nullid
466
467 requests.append(changegroup.revisiondeltarequest(
468 node=node,
469 p1node=parents[0],
470 p2node=parents[1],
471 # Receiver deals with linknode resolution.
472 linknode=nullid,
473 basenode=basenode,
474 ))
475
476 return revs, requests
477
414 478 def wireprotocommand(name, args=None, permission='push'):
415 479 """Decorator to declare a wire protocol command.
416 480
417 481 ``name`` is the name of the wire protocol command being provided.
418 482
419 483 ``args`` is a dict of argument names to example values.
420 484
421 485 ``permission`` defines the permission type needed to run this command.
422 486 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
423 487 respectively. Default is to assume command requires ``push`` permissions
424 488 because otherwise commands not declaring their permissions could modify
425 489 a repository that is supposed to be read-only.
426 490
427 491 Wire protocol commands are generators of objects to be serialized and
428 492 sent to the client.
429 493
430 494 If a command raises an uncaught exception, this will be translated into
431 495 a command error.
432 496 """
433 497 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
434 498 if v['version'] == 2}
435 499
436 500 if permission not in ('push', 'pull'):
437 501 raise error.ProgrammingError('invalid wire protocol permission; '
438 502 'got %s; expected "push" or "pull"' %
439 503 permission)
440 504
441 505 if args is None:
442 506 args = {}
443 507
444 508 if not isinstance(args, dict):
445 509 raise error.ProgrammingError('arguments for version 2 commands '
446 510 'must be declared as dicts')
447 511
448 512 def register(func):
449 513 if name in COMMANDS:
450 514 raise error.ProgrammingError('%s command already registered '
451 515 'for version 2' % name)
452 516
453 517 COMMANDS[name] = wireprototypes.commandentry(
454 518 func, args=args, transports=transports, permission=permission)
455 519
456 520 return func
457 521
458 522 return register
459 523
460 524 @wireprotocommand('branchmap', permission='pull')
461 525 def branchmapv2(repo, proto):
462 526 yield {encoding.fromlocal(k): v
463 527 for k, v in repo.branchmap().iteritems()}
464 528
465 529 @wireprotocommand('capabilities', permission='pull')
466 530 def capabilitiesv2(repo, proto):
467 531 yield _capabilitiesv2(repo, proto)
468 532
469 533 @wireprotocommand('changesetdata',
470 534 args={
471 535 'noderange': [[b'0123456...'], [b'abcdef...']],
472 536 'nodes': [b'0123456...'],
473 537 'fields': {b'parents', b'revision'},
474 538 },
475 539 permission='pull')
476 540 def changesetdata(repo, proto, noderange=None, nodes=None, fields=None):
477 541 fields = fields or set()
478 542
479 543 # TODO look for unknown fields and abort when they can't be serviced.
480 544
481 545 if noderange is None and nodes is None:
482 546 raise error.WireprotoCommandError(
483 547 'noderange or nodes must be defined')
484 548
485 549 if noderange is not None:
486 550 if len(noderange) != 2:
487 551 raise error.WireprotoCommandError(
488 552 'noderange must consist of 2 elements')
489 553
490 554 if not noderange[1]:
491 555 raise error.WireprotoCommandError(
492 556 'heads in noderange request cannot be empty')
493 557
494 558 cl = repo.changelog
495 559 hasnode = cl.hasnode
496 560
497 561 seen = set()
498 562 outgoing = []
499 563
500 564 if nodes is not None:
501 565 outgoing.extend(n for n in nodes if hasnode(n))
502 566 seen |= set(outgoing)
503 567
504 568 if noderange is not None:
505 569 if noderange[0]:
506 570 common = [n for n in noderange[0] if hasnode(n)]
507 571 else:
508 572 common = [nullid]
509 573
510 574 for n in discovery.outgoing(repo, common, noderange[1]).missing:
511 575 if n not in seen:
512 576 outgoing.append(n)
513 577 # Don't need to add to seen here because this is the final
514 578 # source of nodes and there should be no duplicates in this
515 579 # list.
516 580
517 581 seen.clear()
518 582 publishing = repo.publishing()
519 583
520 584 if outgoing:
521 585 repo.hook('preoutgoing', throw=True, source='serve')
522 586
523 587 yield {
524 588 b'totalitems': len(outgoing),
525 589 }
526 590
527 591 # The phases of nodes already transferred to the client may have changed
528 592 # since the client last requested data. We send phase-only records
529 593 # for these revisions, if requested.
530 594 if b'phase' in fields and noderange is not None:
531 595 # TODO skip nodes whose phase will be reflected by a node in the
532 596 # outgoing set. This is purely an optimization to reduce data
533 597 # size.
534 598 for node in noderange[0]:
535 599 yield {
536 600 b'node': node,
537 601 b'phase': b'public' if publishing else repo[node].phasestr()
538 602 }
539 603
540 604 nodebookmarks = {}
541 605 for mark, node in repo._bookmarks.items():
542 606 nodebookmarks.setdefault(node, set()).add(mark)
543 607
544 608 # It is already topologically sorted by revision number.
545 609 for node in outgoing:
546 610 d = {
547 611 b'node': node,
548 612 }
549 613
550 614 if b'parents' in fields:
551 615 d[b'parents'] = cl.parents(node)
552 616
553 617 if b'phase' in fields:
554 618 if publishing:
555 619 d[b'phase'] = b'public'
556 620 else:
557 621 ctx = repo[node]
558 622 d[b'phase'] = ctx.phasestr()
559 623
560 624 if b'bookmarks' in fields and node in nodebookmarks:
561 625 d[b'bookmarks'] = sorted(nodebookmarks[node])
562 626 del nodebookmarks[node]
563 627
564 628 revisiondata = None
565 629
566 630 if b'revision' in fields:
567 631 revisiondata = cl.revision(node, raw=True)
568 632 d[b'revisionsize'] = len(revisiondata)
569 633
570 634 # TODO make it possible for extensions to wrap a function or register
571 635 # a handler to service custom fields.
572 636
573 637 yield d
574 638
575 639 if revisiondata is not None:
576 640 yield revisiondata
577 641
578 642 # If requested, send bookmarks from nodes that didn't have revision
579 643 # data sent so receiver is aware of any bookmark updates.
580 644 if b'bookmarks' in fields:
581 645 for node, marks in sorted(nodebookmarks.iteritems()):
582 646 yield {
583 647 b'node': node,
584 648 b'bookmarks': sorted(marks),
585 649 }
586 650
587 651 @wireprotocommand('heads',
588 652 args={
589 653 'publiconly': False,
590 654 },
591 655 permission='pull')
592 656 def headsv2(repo, proto, publiconly=False):
593 657 if publiconly:
594 658 repo = repo.filtered('immutable')
595 659
596 660 yield repo.heads()
597 661
598 662 @wireprotocommand('known',
599 663 args={
600 664 'nodes': [b'deadbeef'],
601 665 },
602 666 permission='pull')
603 667 def knownv2(repo, proto, nodes=None):
604 668 nodes = nodes or []
605 669 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
606 670 yield result
607 671
608 672 @wireprotocommand('listkeys',
609 673 args={
610 674 'namespace': b'ns',
611 675 },
612 676 permission='pull')
613 677 def listkeysv2(repo, proto, namespace=None):
614 678 keys = repo.listkeys(encoding.tolocal(namespace))
615 679 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
616 680 for k, v in keys.iteritems()}
617 681
618 682 yield keys
619 683
620 684 @wireprotocommand('lookup',
621 685 args={
622 686 'key': b'foo',
623 687 },
624 688 permission='pull')
625 689 def lookupv2(repo, proto, key):
626 690 key = encoding.tolocal(key)
627 691
628 692 # TODO handle exception.
629 693 node = repo.lookup(key)
630 694
631 695 yield node
632 696
697 @wireprotocommand('manifestdata',
698 args={
699 'nodes': [b'0123456...'],
700 'fields': [b'parents', b'revision'],
701 'tree': b'',
702 },
703 permission='pull')
704 def manifestdata(repo, proto, nodes=None, fields=None, tree=None):
705 fields = fields or set()
706
707 if nodes is None:
708 raise error.WireprotoCommandError(
709 'nodes argument must be defined')
710
711 if tree is None:
712 raise error.WireprotoCommandError(
713 'tree argument must be defined')
714
715 store = repo.manifestlog.getstorage(tree)
716
717 # Validate the node is known and abort on unknown revisions.
718 for node in nodes:
719 try:
720 store.rev(node)
721 except error.LookupError:
722 raise error.WireprotoCommandError(
723 'unknown node: %s', (node,))
724
725 revs, requests = builddeltarequests(store, nodes)
726
727 yield {
728 b'totalitems': len(revs),
729 }
730
731 if b'revision' in fields:
732 deltas = store.emitrevisiondeltas(requests)
733 else:
734 deltas = None
735
736 for rev in revs:
737 node = store.node(rev)
738
739 if deltas is not None:
740 delta = next(deltas)
741 else:
742 delta = None
743
744 d = {
745 b'node': node,
746 }
747
748 if b'parents' in fields:
749 d[b'parents'] = store.parents(node)
750
751 if b'revision' in fields:
752 assert delta is not None
753 assert delta.flags == 0
754 assert d[b'node'] == delta.node
755
756 if delta.revision is not None:
757 revisiondata = delta.revision
758 d[b'revisionsize'] = len(revisiondata)
759 else:
760 d[b'deltabasenode'] = delta.basenode
761 revisiondata = delta.delta
762 d[b'deltasize'] = len(revisiondata)
763 else:
764 revisiondata = None
765
766 yield d
767
768 if revisiondata is not None:
769 yield revisiondata
770
771 if deltas is not None:
772 try:
773 next(deltas)
774 raise error.ProgrammingError('should not have more deltas')
775 except GeneratorExit:
776 pass
777
633 778 @wireprotocommand('pushkey',
634 779 args={
635 780 'namespace': b'ns',
636 781 'key': b'key',
637 782 'old': b'old',
638 783 'new': b'new',
639 784 },
640 785 permission='push')
641 786 def pushkeyv2(repo, proto, namespace, key, old, new):
642 787 # TODO handle ui output redirection
643 788 yield repo.pushkey(encoding.tolocal(namespace),
644 789 encoding.tolocal(key),
645 790 encoding.tolocal(old),
646 791 encoding.tolocal(new))
@@ -1,749 +1,749 b''
1 1 #require no-chg
2 2
3 3 $ . $TESTDIR/wireprotohelpers.sh
4 4
5 5 $ cat >> $HGRCPATH << EOF
6 6 > [web]
7 7 > push_ssl = false
8 8 > allow_push = *
9 9 > EOF
10 10
11 11 $ hg init server
12 12 $ cd server
13 13 $ touch a
14 14 $ hg -q commit -A -m initial
15 15 $ cd ..
16 16
17 17 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
18 18 $ cat hg.pid >> $DAEMON_PIDS
19 19
20 20 compression formats are advertised in compression capability
21 21
22 22 #if zstd
23 23 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zstd,zlib$' > /dev/null
24 24 #else
25 25 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zlib$' > /dev/null
26 26 #endif
27 27
28 28 $ killdaemons.py
29 29
30 30 server.compressionengines can replace engines list wholesale
31 31
32 32 $ hg serve --config server.compressionengines=none -R server -p $HGPORT -d --pid-file hg.pid
33 33 $ cat hg.pid > $DAEMON_PIDS
34 34 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none$' > /dev/null
35 35
36 36 $ killdaemons.py
37 37
38 38 Order of engines can also change
39 39
40 40 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
41 41 $ cat hg.pid > $DAEMON_PIDS
42 42 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none,zlib$' > /dev/null
43 43
44 44 $ killdaemons.py
45 45
46 46 Start a default server again
47 47
48 48 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
49 49 $ cat hg.pid > $DAEMON_PIDS
50 50
51 51 Server should send application/mercurial-0.1 to clients if no Accept is used
52 52
53 53 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
54 54 200 Script output follows
55 55 content-type: application/mercurial-0.1
56 56 date: $HTTP_DATE$
57 57 server: testing stub value
58 58 transfer-encoding: chunked
59 59
60 60 Server should send application/mercurial-0.1 when client says it wants it
61 61
62 62 $ get-with-headers.py --hgproto '0.1' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
63 63 200 Script output follows
64 64 content-type: application/mercurial-0.1
65 65 date: $HTTP_DATE$
66 66 server: testing stub value
67 67 transfer-encoding: chunked
68 68
69 69 Server should send application/mercurial-0.2 when client says it wants it
70 70
71 71 $ get-with-headers.py --hgproto '0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
72 72 200 Script output follows
73 73 content-type: application/mercurial-0.2
74 74 date: $HTTP_DATE$
75 75 server: testing stub value
76 76 transfer-encoding: chunked
77 77
78 78 $ get-with-headers.py --hgproto '0.1 0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
79 79 200 Script output follows
80 80 content-type: application/mercurial-0.2
81 81 date: $HTTP_DATE$
82 82 server: testing stub value
83 83 transfer-encoding: chunked
84 84
85 85 Requesting a compression format that server doesn't support results will fall back to 0.1
86 86
87 87 $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
88 88 200 Script output follows
89 89 content-type: application/mercurial-0.1
90 90 date: $HTTP_DATE$
91 91 server: testing stub value
92 92 transfer-encoding: chunked
93 93
94 94 #if zstd
95 95 zstd is used if available
96 96
97 97 $ get-with-headers.py --hgproto '0.2 comp=zstd' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
98 98 $ f --size --hexdump --bytes 36 --sha1 resp
99 99 resp: size=248, sha1=4d8d8f87fb82bd542ce52881fdc94f850748
100 100 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
101 101 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 73 74 64 |t follows...zstd|
102 102 0020: 28 b5 2f fd |(./.|
103 103
104 104 #endif
105 105
106 106 application/mercurial-0.2 is not yet used on non-streaming responses
107 107
108 108 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=heads' -
109 109 200 Script output follows
110 110 content-length: 41
111 111 content-type: application/mercurial-0.1
112 112 date: $HTTP_DATE$
113 113 server: testing stub value
114 114
115 115 e93700bd72895c5addab234c56d4024b487a362f
116 116
117 117 Now test protocol preference usage
118 118
119 119 $ killdaemons.py
120 120 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
121 121 $ cat hg.pid > $DAEMON_PIDS
122 122
123 123 No Accept will send 0.1+zlib, even though "none" is preferred b/c "none" isn't supported on 0.1
124 124
125 125 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' Content-Type
126 126 200 Script output follows
127 127 content-type: application/mercurial-0.1
128 128
129 129 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
130 130 $ f --size --hexdump --bytes 28 --sha1 resp
131 131 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
132 132 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
133 133 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
134 134
135 135 Explicit 0.1 will send zlib because "none" isn't supported on 0.1
136 136
137 137 $ get-with-headers.py --hgproto '0.1' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
138 138 $ f --size --hexdump --bytes 28 --sha1 resp
139 139 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
140 140 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
141 141 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
142 142
143 143 0.2 with no compression will get "none" because that is server's preference
144 144 (spec says ZL and UN are implicitly supported)
145 145
146 146 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
147 147 $ f --size --hexdump --bytes 32 --sha1 resp
148 148 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
149 149 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
150 150 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
151 151
152 152 Client receives server preference even if local order doesn't match
153 153
154 154 $ get-with-headers.py --hgproto '0.2 comp=zlib,none' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
155 155 $ f --size --hexdump --bytes 32 --sha1 resp
156 156 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
157 157 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
158 158 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
159 159
160 160 Client receives only supported format even if not server preferred format
161 161
162 162 $ get-with-headers.py --hgproto '0.2 comp=zlib' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
163 163 $ f --size --hexdump --bytes 33 --sha1 resp
164 164 resp: size=232, sha1=a1c727f0c9693ca15742a75c30419bc36
165 165 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
166 166 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib|
167 167 0020: 78 |x|
168 168
169 169 $ killdaemons.py
170 170 $ cd ..
171 171
172 172 Test listkeys for listing namespaces
173 173
174 174 $ hg init empty
175 175 $ hg -R empty serve -p $HGPORT -d --pid-file hg.pid
176 176 $ cat hg.pid > $DAEMON_PIDS
177 177
178 178 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
179 179 > command listkeys
180 180 > namespace namespaces
181 181 > EOF
182 182 s> GET /?cmd=capabilities HTTP/1.1\r\n
183 183 s> Accept-Encoding: identity\r\n
184 184 s> accept: application/mercurial-0.1\r\n
185 185 s> host: $LOCALIP:$HGPORT\r\n (glob)
186 186 s> user-agent: Mercurial debugwireproto\r\n
187 187 s> \r\n
188 188 s> makefile('rb', None)
189 189 s> HTTP/1.1 200 Script output follows\r\n
190 190 s> Server: testing stub value\r\n
191 191 s> Date: $HTTP_DATE$\r\n
192 192 s> Content-Type: application/mercurial-0.1\r\n
193 193 s> Content-Length: *\r\n (glob)
194 194 s> \r\n
195 195 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
196 196 sending listkeys command
197 197 s> GET /?cmd=listkeys HTTP/1.1\r\n
198 198 s> Accept-Encoding: identity\r\n
199 199 s> vary: X-HgArg-1,X-HgProto-1\r\n
200 200 s> x-hgarg-1: namespace=namespaces\r\n
201 201 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
202 202 s> accept: application/mercurial-0.1\r\n
203 203 s> host: $LOCALIP:$HGPORT\r\n (glob)
204 204 s> user-agent: Mercurial debugwireproto\r\n
205 205 s> \r\n
206 206 s> makefile('rb', None)
207 207 s> HTTP/1.1 200 Script output follows\r\n
208 208 s> Server: testing stub value\r\n
209 209 s> Date: $HTTP_DATE$\r\n
210 210 s> Content-Type: application/mercurial-0.1\r\n
211 211 s> Content-Length: 30\r\n
212 212 s> \r\n
213 213 s> bookmarks\t\n
214 214 s> namespaces\t\n
215 215 s> phases\t
216 216 response: {
217 217 b'bookmarks': b'',
218 218 b'namespaces': b'',
219 219 b'phases': b''
220 220 }
221 221
222 222 Same thing, but with "httprequest" command
223 223
224 224 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
225 225 > httprequest GET ?cmd=listkeys
226 226 > user-agent: test
227 227 > x-hgarg-1: namespace=namespaces
228 228 > EOF
229 229 using raw connection to peer
230 230 s> GET /?cmd=listkeys HTTP/1.1\r\n
231 231 s> Accept-Encoding: identity\r\n
232 232 s> user-agent: test\r\n
233 233 s> x-hgarg-1: namespace=namespaces\r\n
234 234 s> host: $LOCALIP:$HGPORT\r\n (glob)
235 235 s> \r\n
236 236 s> makefile('rb', None)
237 237 s> HTTP/1.1 200 Script output follows\r\n
238 238 s> Server: testing stub value\r\n
239 239 s> Date: $HTTP_DATE$\r\n
240 240 s> Content-Type: application/mercurial-0.1\r\n
241 241 s> Content-Length: 30\r\n
242 242 s> \r\n
243 243 s> bookmarks\t\n
244 244 s> namespaces\t\n
245 245 s> phases\t
246 246
247 247 Client with HTTPv2 enabled advertises that and gets old capabilities response from old server
248 248
249 249 $ hg --config experimental.httppeer.advertise-v2=true --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
250 250 > command heads
251 251 > EOF
252 252 s> GET /?cmd=capabilities HTTP/1.1\r\n
253 253 s> Accept-Encoding: identity\r\n
254 254 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
255 255 s> x-hgproto-1: cbor\r\n
256 256 s> x-hgupgrade-1: exp-http-v2-0001\r\n
257 257 s> accept: application/mercurial-0.1\r\n
258 258 s> host: $LOCALIP:$HGPORT\r\n (glob)
259 259 s> user-agent: Mercurial debugwireproto\r\n
260 260 s> \r\n
261 261 s> makefile('rb', None)
262 262 s> HTTP/1.1 200 Script output follows\r\n
263 263 s> Server: testing stub value\r\n
264 264 s> Date: $HTTP_DATE$\r\n
265 265 s> Content-Type: application/mercurial-0.1\r\n
266 266 s> Content-Length: *\r\n (glob)
267 267 s> \r\n
268 268 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
269 269 sending heads command
270 270 s> GET /?cmd=heads HTTP/1.1\r\n
271 271 s> Accept-Encoding: identity\r\n
272 272 s> vary: X-HgProto-1\r\n
273 273 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
274 274 s> accept: application/mercurial-0.1\r\n
275 275 s> host: $LOCALIP:$HGPORT\r\n (glob)
276 276 s> user-agent: Mercurial debugwireproto\r\n
277 277 s> \r\n
278 278 s> makefile('rb', None)
279 279 s> HTTP/1.1 200 Script output follows\r\n
280 280 s> Server: testing stub value\r\n
281 281 s> Date: $HTTP_DATE$\r\n
282 282 s> Content-Type: application/mercurial-0.1\r\n
283 283 s> Content-Length: 41\r\n
284 284 s> \r\n
285 285 s> 0000000000000000000000000000000000000000\n
286 286 response: [
287 287 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
288 288 ]
289 289
290 290 $ killdaemons.py
291 291 $ enablehttpv2 empty
292 292 $ hg --config server.compressionengines=zlib -R empty serve -p $HGPORT -d --pid-file hg.pid
293 293 $ cat hg.pid > $DAEMON_PIDS
294 294
295 295 Client with HTTPv2 enabled automatically upgrades if the server supports it
296 296
297 297 $ hg --config experimental.httppeer.advertise-v2=true --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
298 298 > command heads
299 299 > EOF
300 300 s> GET /?cmd=capabilities HTTP/1.1\r\n
301 301 s> Accept-Encoding: identity\r\n
302 302 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
303 303 s> x-hgproto-1: cbor\r\n
304 304 s> x-hgupgrade-1: exp-http-v2-0001\r\n
305 305 s> accept: application/mercurial-0.1\r\n
306 306 s> host: $LOCALIP:$HGPORT\r\n (glob)
307 307 s> user-agent: Mercurial debugwireproto\r\n
308 308 s> \r\n
309 309 s> makefile('rb', None)
310 310 s> HTTP/1.1 200 OK\r\n
311 311 s> Server: testing stub value\r\n
312 312 s> Date: $HTTP_DATE$\r\n
313 313 s> Content-Type: application/mercurial-cbor\r\n
314 314 s> Content-Length: *\r\n (glob)
315 315 s> \r\n
316 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa8Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\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
316 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa9Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa3Ffields\x82GparentsHrevisionEnodes\x81J0123456...Dtree@Kpermissions\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
317 317 sending heads command
318 318 s> POST /api/exp-http-v2-0001/ro/heads HTTP/1.1\r\n
319 319 s> Accept-Encoding: identity\r\n
320 320 s> accept: application/mercurial-exp-framing-0005\r\n
321 321 s> content-type: application/mercurial-exp-framing-0005\r\n
322 322 s> content-length: 20\r\n
323 323 s> host: $LOCALIP:$HGPORT\r\n (glob)
324 324 s> user-agent: Mercurial debugwireproto\r\n
325 325 s> \r\n
326 326 s> \x0c\x00\x00\x01\x00\x01\x01\x11\xa1DnameEheads
327 327 s> makefile('rb', None)
328 328 s> HTTP/1.1 200 OK\r\n
329 329 s> Server: testing stub value\r\n
330 330 s> Date: $HTTP_DATE$\r\n
331 331 s> Content-Type: application/mercurial-exp-framing-0005\r\n
332 332 s> Transfer-Encoding: chunked\r\n
333 333 s> \r\n
334 334 s> 13\r\n
335 335 s> \x0b\x00\x00\x01\x00\x02\x011
336 336 s> \xa1FstatusBok
337 337 s> \r\n
338 338 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
339 339 s> 1e\r\n
340 340 s> \x16\x00\x00\x01\x00\x02\x001
341 341 s> \x81T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
342 342 s> \r\n
343 343 received frame(size=22; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
344 344 s> 8\r\n
345 345 s> \x00\x00\x00\x01\x00\x02\x002
346 346 s> \r\n
347 347 s> 0\r\n
348 348 s> \r\n
349 349 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
350 350 response: [
351 351 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
352 352 ]
353 353
354 354 $ killdaemons.py
355 355
356 356 HTTP client follows HTTP redirect on handshake to new repo
357 357
358 358 $ cd $TESTTMP
359 359
360 360 $ hg init redirector
361 361 $ hg init redirected
362 362 $ cd redirected
363 363 $ touch foo
364 364 $ hg -q commit -A -m initial
365 365 $ cd ..
366 366
367 367 $ cat > paths.conf << EOF
368 368 > [paths]
369 369 > / = $TESTTMP/*
370 370 > EOF
371 371
372 372 $ cat > redirectext.py << EOF
373 373 > from mercurial import extensions, wireprotoserver
374 374 > def wrappedcallhttp(orig, repo, req, res, proto, cmd):
375 375 > path = req.advertisedurl[len(req.advertisedbaseurl):]
376 376 > if not path.startswith(b'/redirector'):
377 377 > return orig(repo, req, res, proto, cmd)
378 378 > relpath = path[len(b'/redirector'):]
379 379 > res.status = b'301 Redirect'
380 380 > newurl = b'%s/redirected%s' % (req.baseurl, relpath)
381 381 > if not repo.ui.configbool('testing', 'redirectqs', True) and b'?' in newurl:
382 382 > newurl = newurl[0:newurl.index(b'?')]
383 383 > res.headers[b'Location'] = newurl
384 384 > res.headers[b'Content-Type'] = b'text/plain'
385 385 > res.setbodybytes(b'redirected')
386 386 > return True
387 387 >
388 388 > extensions.wrapfunction(wireprotoserver, '_callhttp', wrappedcallhttp)
389 389 > EOF
390 390
391 391 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
392 392 > --config server.compressionengines=zlib \
393 393 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
394 394 $ cat hg.pid > $DAEMON_PIDS
395 395
396 396 Verify our HTTP 301 is served properly
397 397
398 398 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
399 399 > httprequest GET /redirector?cmd=capabilities
400 400 > user-agent: test
401 401 > EOF
402 402 using raw connection to peer
403 403 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
404 404 s> Accept-Encoding: identity\r\n
405 405 s> user-agent: test\r\n
406 406 s> host: $LOCALIP:$HGPORT\r\n (glob)
407 407 s> \r\n
408 408 s> makefile('rb', None)
409 409 s> HTTP/1.1 301 Redirect\r\n
410 410 s> Server: testing stub value\r\n
411 411 s> Date: $HTTP_DATE$\r\n
412 412 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
413 413 s> Content-Type: text/plain\r\n
414 414 s> Content-Length: 10\r\n
415 415 s> \r\n
416 416 s> redirected
417 417 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
418 418 s> Accept-Encoding: identity\r\n
419 419 s> user-agent: test\r\n
420 420 s> host: $LOCALIP:$HGPORT\r\n (glob)
421 421 s> \r\n
422 422 s> makefile('rb', None)
423 423 s> HTTP/1.1 200 Script output follows\r\n
424 424 s> Server: testing stub value\r\n
425 425 s> Date: $HTTP_DATE$\r\n
426 426 s> Content-Type: application/mercurial-0.1\r\n
427 427 s> Content-Length: 453\r\n
428 428 s> \r\n
429 429 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
430 430
431 431 Test with the HTTP peer
432 432
433 433 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
434 434 > command heads
435 435 > EOF
436 436 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
437 437 s> Accept-Encoding: identity\r\n
438 438 s> accept: application/mercurial-0.1\r\n
439 439 s> host: $LOCALIP:$HGPORT\r\n (glob)
440 440 s> user-agent: Mercurial debugwireproto\r\n
441 441 s> \r\n
442 442 s> makefile('rb', None)
443 443 s> HTTP/1.1 301 Redirect\r\n
444 444 s> Server: testing stub value\r\n
445 445 s> Date: $HTTP_DATE$\r\n
446 446 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
447 447 s> Content-Type: text/plain\r\n
448 448 s> Content-Length: 10\r\n
449 449 s> \r\n
450 450 s> redirected
451 451 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
452 452 s> Accept-Encoding: identity\r\n
453 453 s> accept: application/mercurial-0.1\r\n
454 454 s> host: $LOCALIP:$HGPORT\r\n (glob)
455 455 s> user-agent: Mercurial debugwireproto\r\n
456 456 s> \r\n
457 457 s> makefile('rb', None)
458 458 s> HTTP/1.1 200 Script output follows\r\n
459 459 s> Server: testing stub value\r\n
460 460 s> Date: $HTTP_DATE$\r\n
461 461 s> Content-Type: application/mercurial-0.1\r\n
462 462 s> Content-Length: 453\r\n
463 463 s> \r\n
464 464 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
465 465 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
466 466 sending heads command
467 467 s> GET /redirected?cmd=heads HTTP/1.1\r\n
468 468 s> Accept-Encoding: identity\r\n
469 469 s> vary: X-HgProto-1\r\n
470 470 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
471 471 s> accept: application/mercurial-0.1\r\n
472 472 s> host: $LOCALIP:$HGPORT\r\n (glob)
473 473 s> user-agent: Mercurial debugwireproto\r\n
474 474 s> \r\n
475 475 s> makefile('rb', None)
476 476 s> HTTP/1.1 200 Script output follows\r\n
477 477 s> Server: testing stub value\r\n
478 478 s> Date: $HTTP_DATE$\r\n
479 479 s> Content-Type: application/mercurial-0.1\r\n
480 480 s> Content-Length: 41\r\n
481 481 s> \r\n
482 482 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
483 483 response: [
484 484 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
485 485 ]
486 486
487 487 $ killdaemons.py
488 488
489 489 Now test a variation where we strip the query string from the redirect URL.
490 490 (SCM Manager apparently did this and clients would recover from it)
491 491
492 492 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
493 493 > --config server.compressionengines=zlib \
494 494 > --config testing.redirectqs=false \
495 495 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
496 496 $ cat hg.pid > $DAEMON_PIDS
497 497
498 498 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
499 499 > httprequest GET /redirector?cmd=capabilities
500 500 > user-agent: test
501 501 > EOF
502 502 using raw connection to peer
503 503 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
504 504 s> Accept-Encoding: identity\r\n
505 505 s> user-agent: test\r\n
506 506 s> host: $LOCALIP:$HGPORT\r\n (glob)
507 507 s> \r\n
508 508 s> makefile('rb', None)
509 509 s> HTTP/1.1 301 Redirect\r\n
510 510 s> Server: testing stub value\r\n
511 511 s> Date: $HTTP_DATE$\r\n
512 512 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
513 513 s> Content-Type: text/plain\r\n
514 514 s> Content-Length: 10\r\n
515 515 s> \r\n
516 516 s> redirected
517 517 s> GET /redirected HTTP/1.1\r\n
518 518 s> Accept-Encoding: identity\r\n
519 519 s> user-agent: test\r\n
520 520 s> host: $LOCALIP:$HGPORT\r\n (glob)
521 521 s> \r\n
522 522 s> makefile('rb', None)
523 523 s> HTTP/1.1 200 Script output follows\r\n
524 524 s> Server: testing stub value\r\n
525 525 s> Date: $HTTP_DATE$\r\n
526 526 s> ETag: W/"*"\r\n (glob)
527 527 s> Content-Type: text/html; charset=ascii\r\n
528 528 s> Transfer-Encoding: chunked\r\n
529 529 s> \r\n
530 530 s> 414\r\n
531 531 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
532 532 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
533 533 s> <head>\n
534 534 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
535 535 s> <meta name="robots" content="index, nofollow" />\n
536 536 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
537 537 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
538 538 s> \n
539 539 s> <title>redirected: log</title>\n
540 540 s> <link rel="alternate" type="application/atom+xml"\n
541 541 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
542 542 s> <link rel="alternate" type="application/rss+xml"\n
543 543 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
544 544 s> </head>\n
545 545 s> <body>\n
546 546 s> \n
547 547 s> <div class="container">\n
548 548 s> <div class="menu">\n
549 549 s> <div class="logo">\n
550 550 s> <a href="https://mercurial-scm.org/">\n
551 551 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
552 552 s> </div>\n
553 553 s> <ul>\n
554 554 s> <li class="active">log</li>\n
555 555 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
556 556 s> <li><a href="/redirected/tags">tags</a></li>\n
557 557 s> <li><a href="
558 558 s> \r\n
559 559 s> 810\r\n
560 560 s> /redirected/bookmarks">bookmarks</a></li>\n
561 561 s> <li><a href="/redirected/branches">branches</a></li>\n
562 562 s> </ul>\n
563 563 s> <ul>\n
564 564 s> <li><a href="/redirected/rev/tip">changeset</a></li>\n
565 565 s> <li><a href="/redirected/file/tip">browse</a></li>\n
566 566 s> </ul>\n
567 567 s> <ul>\n
568 568 s> \n
569 569 s> </ul>\n
570 570 s> <ul>\n
571 571 s> <li><a href="/redirected/help">help</a></li>\n
572 572 s> </ul>\n
573 573 s> <div class="atom-logo">\n
574 574 s> <a href="/redirected/atom-log" title="subscribe to atom feed">\n
575 575 s> <img class="atom-logo" src="/redirected/static/feed-icon-14x14.png" alt="atom feed" />\n
576 576 s> </a>\n
577 577 s> </div>\n
578 578 s> </div>\n
579 579 s> \n
580 580 s> <div class="main">\n
581 581 s> <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/redirected">redirected</a> </h2>\n
582 582 s> <h3>log</h3>\n
583 583 s> \n
584 584 s> \n
585 585 s> <form class="search" action="/redirected/log">\n
586 586 s> \n
587 587 s> <p><input name="rev" id="search1" type="text" size="30" value="" /></p>\n
588 588 s> <div id="hint">Find changesets by keywords (author, files, the commit message), revision\n
589 589 s> number or hash, or <a href="/redirected/help/revsets">revset expression</a>.</div>\n
590 590 s> </form>\n
591 591 s> \n
592 592 s> <div class="navigate">\n
593 593 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
594 594 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
595 595 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
596 596 s> </div>\n
597 597 s> \n
598 598 s> <table class="bigtable">\n
599 599 s> <thead>\n
600 600 s> <tr>\n
601 601 s> <th class="age">age</th>\n
602 602 s> <th class="author">author</th>\n
603 603 s> <th class="description">description</th>\n
604 604 s> </tr>\n
605 605 s> </thead>\n
606 606 s> <tbody class="stripes2">\n
607 607 s> <tr>\n
608 608 s> <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>\n
609 609 s> <td class="author">test</td>\n
610 610 s> <td class="description">\n
611 611 s> <a href="/redirected/rev/96ee1d7354c4">initial</a>\n
612 612 s> <span class="phase">draft</span> <span class="branchhead">default</span> <span class="tag">tip</span> \n
613 613 s> </td>\n
614 614 s> </tr>\n
615 615 s> \n
616 616 s> </tbody>\n
617 617 s> </table>\n
618 618 s> \n
619 619 s> <div class="navigate">\n
620 620 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
621 621 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
622 622 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
623 623 s> </div>\n
624 624 s> \n
625 625 s> <script type="text/javascript">\n
626 626 s> ajaxScrollInit(\n
627 627 s> \'/redirected/shortlog/%next%\',\n
628 628 s> \'\', <!-- NEXTHASH\n
629 629 s> function (htmlText) {
630 630 s> \r\n
631 631 s> 14a\r\n
632 632 s> \n
633 633 s> var m = htmlText.match(/\'(\\w+)\', <!-- NEXTHASH/);\n
634 634 s> return m ? m[1] : null;\n
635 635 s> },\n
636 636 s> \'.bigtable > tbody\',\n
637 637 s> \'<tr class="%class%">\\\n
638 638 s> <td colspan="3" style="text-align: center;">%text%</td>\\\n
639 639 s> </tr>\'\n
640 640 s> );\n
641 641 s> </script>\n
642 642 s> \n
643 643 s> </div>\n
644 644 s> </div>\n
645 645 s> \n
646 646 s> \n
647 647 s> \n
648 648 s> </body>\n
649 649 s> </html>\n
650 650 s> \n
651 651 s> \r\n
652 652 s> 0\r\n
653 653 s> \r\n
654 654
655 655 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
656 656 > command heads
657 657 > EOF
658 658 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
659 659 s> Accept-Encoding: identity\r\n
660 660 s> accept: application/mercurial-0.1\r\n
661 661 s> host: $LOCALIP:$HGPORT\r\n (glob)
662 662 s> user-agent: Mercurial debugwireproto\r\n
663 663 s> \r\n
664 664 s> makefile('rb', None)
665 665 s> HTTP/1.1 301 Redirect\r\n
666 666 s> Server: testing stub value\r\n
667 667 s> Date: $HTTP_DATE$\r\n
668 668 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
669 669 s> Content-Type: text/plain\r\n
670 670 s> Content-Length: 10\r\n
671 671 s> \r\n
672 672 s> redirected
673 673 s> GET /redirected HTTP/1.1\r\n
674 674 s> Accept-Encoding: identity\r\n
675 675 s> accept: application/mercurial-0.1\r\n
676 676 s> host: $LOCALIP:$HGPORT\r\n (glob)
677 677 s> user-agent: Mercurial debugwireproto\r\n
678 678 s> \r\n
679 679 s> makefile('rb', None)
680 680 s> HTTP/1.1 200 Script output follows\r\n
681 681 s> Server: testing stub value\r\n
682 682 s> Date: $HTTP_DATE$\r\n
683 683 s> ETag: W/"*"\r\n (glob)
684 684 s> Content-Type: text/html; charset=ascii\r\n
685 685 s> Transfer-Encoding: chunked\r\n
686 686 s> \r\n
687 687 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
688 688 s> 414\r\n
689 689 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
690 690 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
691 691 s> <head>\n
692 692 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
693 693 s> <meta name="robots" content="index, nofollow" />\n
694 694 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
695 695 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
696 696 s> \n
697 697 s> <title>redirected: log</title>\n
698 698 s> <link rel="alternate" type="application/atom+xml"\n
699 699 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
700 700 s> <link rel="alternate" type="application/rss+xml"\n
701 701 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
702 702 s> </head>\n
703 703 s> <body>\n
704 704 s> \n
705 705 s> <div class="container">\n
706 706 s> <div class="menu">\n
707 707 s> <div class="logo">\n
708 708 s> <a href="https://mercurial-scm.org/">\n
709 709 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
710 710 s> </div>\n
711 711 s> <ul>\n
712 712 s> <li class="active">log</li>\n
713 713 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
714 714 s> <li><a href="/redirected/tags">tags</a
715 715 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
716 716 s> Accept-Encoding: identity\r\n
717 717 s> accept: application/mercurial-0.1\r\n
718 718 s> host: $LOCALIP:$HGPORT\r\n (glob)
719 719 s> user-agent: Mercurial debugwireproto\r\n
720 720 s> \r\n
721 721 s> makefile('rb', None)
722 722 s> HTTP/1.1 200 Script output follows\r\n
723 723 s> Server: testing stub value\r\n
724 724 s> Date: $HTTP_DATE$\r\n
725 725 s> Content-Type: application/mercurial-0.1\r\n
726 726 s> Content-Length: 453\r\n
727 727 s> \r\n
728 728 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
729 729 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
730 730 sending heads command
731 731 s> GET /redirected?cmd=heads HTTP/1.1\r\n
732 732 s> Accept-Encoding: identity\r\n
733 733 s> vary: X-HgProto-1\r\n
734 734 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
735 735 s> accept: application/mercurial-0.1\r\n
736 736 s> host: $LOCALIP:$HGPORT\r\n (glob)
737 737 s> user-agent: Mercurial debugwireproto\r\n
738 738 s> \r\n
739 739 s> makefile('rb', None)
740 740 s> HTTP/1.1 200 Script output follows\r\n
741 741 s> Server: testing stub value\r\n
742 742 s> Date: $HTTP_DATE$\r\n
743 743 s> Content-Type: application/mercurial-0.1\r\n
744 744 s> Content-Length: 41\r\n
745 745 s> \r\n
746 746 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
747 747 response: [
748 748 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
749 749 ]
@@ -1,472 +1,502 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 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa8Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\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
215 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa9Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa3Ffields\x82GparentsHrevisionEnodes\x81J0123456...Dtree@Kpermissions\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'changesetdata': {
234 234 b'args': {
235 235 b'fields': set([
236 236 b'parents',
237 237 b'revision'
238 238 ]),
239 239 b'noderange': [
240 240 [
241 241 b'0123456...'
242 242 ],
243 243 [
244 244 b'abcdef...'
245 245 ]
246 246 ],
247 247 b'nodes': [
248 248 b'0123456...'
249 249 ]
250 250 },
251 251 b'permissions': [
252 252 b'pull'
253 253 ]
254 254 },
255 255 b'heads': {
256 256 b'args': {
257 257 b'publiconly': False
258 258 },
259 259 b'permissions': [
260 260 b'pull'
261 261 ]
262 262 },
263 263 b'known': {
264 264 b'args': {
265 265 b'nodes': [
266 266 b'deadbeef'
267 267 ]
268 268 },
269 269 b'permissions': [
270 270 b'pull'
271 271 ]
272 272 },
273 273 b'listkeys': {
274 274 b'args': {
275 275 b'namespace': b'ns'
276 276 },
277 277 b'permissions': [
278 278 b'pull'
279 279 ]
280 280 },
281 281 b'lookup': {
282 282 b'args': {
283 283 b'key': b'foo'
284 284 },
285 285 b'permissions': [
286 286 b'pull'
287 287 ]
288 288 },
289 b'manifestdata': {
290 b'args': {
291 b'fields': [
292 b'parents',
293 b'revision'
294 ],
295 b'nodes': [
296 b'0123456...'
297 ],
298 b'tree': b''
299 },
300 b'permissions': [
301 b'pull'
302 ]
303 },
289 304 b'pushkey': {
290 305 b'args': {
291 306 b'key': b'key',
292 307 b'namespace': b'ns',
293 308 b'new': b'new',
294 309 b'old': b'old'
295 310 },
296 311 b'permissions': [
297 312 b'push'
298 313 ]
299 314 }
300 315 },
301 316 b'compression': [
302 317 {
303 318 b'name': b'zlib'
304 319 }
305 320 ],
306 321 b'framingmediatypes': [
307 322 b'application/mercurial-exp-framing-0005'
308 323 ],
309 324 b'rawrepoformats': [
310 325 b'generaldelta',
311 326 b'revlogv1'
312 327 ]
313 328 }
314 329 },
315 330 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'
316 331 }
317 332
318 333 capabilities command returns expected info
319 334
320 335 $ sendhttpv2peerhandshake << EOF
321 336 > command capabilities
322 337 > EOF
323 338 creating http peer for wire protocol version 2
324 339 s> GET /?cmd=capabilities HTTP/1.1\r\n
325 340 s> Accept-Encoding: identity\r\n
326 341 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
327 342 s> x-hgproto-1: cbor\r\n
328 343 s> x-hgupgrade-1: exp-http-v2-0001\r\n
329 344 s> accept: application/mercurial-0.1\r\n
330 345 s> host: $LOCALIP:$HGPORT\r\n (glob)
331 346 s> user-agent: Mercurial debugwireproto\r\n
332 347 s> \r\n
333 348 s> makefile('rb', None)
334 349 s> HTTP/1.1 200 OK\r\n
335 350 s> Server: testing stub value\r\n
336 351 s> Date: $HTTP_DATE$\r\n
337 352 s> Content-Type: application/mercurial-cbor\r\n
338 353 s> Content-Length: *\r\n (glob)
339 354 s> \r\n
340 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa8Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\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
355 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa9Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa3Ffields\x82GparentsHrevisionEnodes\x81J0123456...Dtree@Kpermissions\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
341 356 sending capabilities command
342 357 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
343 358 s> Accept-Encoding: identity\r\n
344 359 s> accept: application/mercurial-exp-framing-0005\r\n
345 360 s> content-type: application/mercurial-exp-framing-0005\r\n
346 361 s> content-length: 27\r\n
347 362 s> host: $LOCALIP:$HGPORT\r\n (glob)
348 363 s> user-agent: Mercurial debugwireproto\r\n
349 364 s> \r\n
350 365 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
351 366 s> makefile('rb', None)
352 367 s> HTTP/1.1 200 OK\r\n
353 368 s> Server: testing stub value\r\n
354 369 s> Date: $HTTP_DATE$\r\n
355 370 s> Content-Type: application/mercurial-exp-framing-0005\r\n
356 371 s> Transfer-Encoding: chunked\r\n
357 372 s> \r\n
358 373 s> 13\r\n
359 374 s> \x0b\x00\x00\x01\x00\x02\x011
360 375 s> \xa1FstatusBok
361 376 s> \r\n
362 377 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
363 s> 243\r\n
364 s> ;\x02\x00\x01\x00\x02\x001
365 s> \xa4Hcommands\xa8Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\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
378 s> 29a\r\n
379 s> \x92\x02\x00\x01\x00\x02\x001
380 s> \xa4Hcommands\xa9Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa3Ffields\x82GparentsHrevisionEnodes\x81J0123456...Dtree@Kpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1
366 381 s> \r\n
367 received frame(size=571; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
382 received frame(size=658; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
368 383 s> 8\r\n
369 384 s> \x00\x00\x00\x01\x00\x02\x002
370 385 s> \r\n
371 386 s> 0\r\n
372 387 s> \r\n
373 388 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
374 389 response: gen[
375 390 {
376 391 b'commands': {
377 392 b'branchmap': {
378 393 b'args': {},
379 394 b'permissions': [
380 395 b'pull'
381 396 ]
382 397 },
383 398 b'capabilities': {
384 399 b'args': {},
385 400 b'permissions': [
386 401 b'pull'
387 402 ]
388 403 },
389 404 b'changesetdata': {
390 405 b'args': {
391 406 b'fields': set([
392 407 b'parents',
393 408 b'revision'
394 409 ]),
395 410 b'noderange': [
396 411 [
397 412 b'0123456...'
398 413 ],
399 414 [
400 415 b'abcdef...'
401 416 ]
402 417 ],
403 418 b'nodes': [
404 419 b'0123456...'
405 420 ]
406 421 },
407 422 b'permissions': [
408 423 b'pull'
409 424 ]
410 425 },
411 426 b'heads': {
412 427 b'args': {
413 428 b'publiconly': False
414 429 },
415 430 b'permissions': [
416 431 b'pull'
417 432 ]
418 433 },
419 434 b'known': {
420 435 b'args': {
421 436 b'nodes': [
422 437 b'deadbeef'
423 438 ]
424 439 },
425 440 b'permissions': [
426 441 b'pull'
427 442 ]
428 443 },
429 444 b'listkeys': {
430 445 b'args': {
431 446 b'namespace': b'ns'
432 447 },
433 448 b'permissions': [
434 449 b'pull'
435 450 ]
436 451 },
437 452 b'lookup': {
438 453 b'args': {
439 454 b'key': b'foo'
440 455 },
441 456 b'permissions': [
442 457 b'pull'
443 458 ]
444 459 },
460 b'manifestdata': {
461 b'args': {
462 b'fields': [
463 b'parents',
464 b'revision'
465 ],
466 b'nodes': [
467 b'0123456...'
468 ],
469 b'tree': b''
470 },
471 b'permissions': [
472 b'pull'
473 ]
474 },
445 475 b'pushkey': {
446 476 b'args': {
447 477 b'key': b'key',
448 478 b'namespace': b'ns',
449 479 b'new': b'new',
450 480 b'old': b'old'
451 481 },
452 482 b'permissions': [
453 483 b'push'
454 484 ]
455 485 }
456 486 },
457 487 b'compression': [
458 488 {
459 489 b'name': b'zlib'
460 490 }
461 491 ],
462 492 b'framingmediatypes': [
463 493 b'application/mercurial-exp-framing-0005'
464 494 ],
465 495 b'rawrepoformats': [
466 496 b'generaldelta',
467 497 b'revlogv1'
468 498 ]
469 499 }
470 500 ]
471 501
472 502 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now