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