##// END OF EJS Templates
wireprotov2: expose rich arguments metadata...
Gregory Szorc -
r39837:8e7e822e default
parent child Browse files
Show More
@@ -1,445 +1,457 b''
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 (map) Describes arguments accepted by the command.
63
64 Keys are bytestrings denoting the argument name.
65
66 Values are maps describing the argument. The map has the following
67 bytestring keys:
63
68
64 Types are defined as a representative value for the expected type.
69 default
65 e.g. an argument expecting a boolean type will have its value
70 (varied) The default value for this argument if not specified. Only
66 set to true. An integer type will have its value set to 42. The
71 present if ``required`` is not true.
67 actual values are arbitrary and may not have meaning.
72
73 required
74 (boolean) Whether the argument must be specified. Failure to send
75 required arguments will result in an error executing the command.
76
77 type
78 (bytestring) The type of the argument. e.g. ``bytes`` or ``bool``.
79
68 permissions
80 permissions
69 An array of permissions required to execute this command.
81 An array of permissions required to execute this command.
70
82
71 compression
83 compression
72 An array of maps defining available compression format support.
84 An array of maps defining available compression format support.
73
85
74 The array is sorted from most preferred to least preferred.
86 The array is sorted from most preferred to least preferred.
75
87
76 Each entry has the following bytestring keys:
88 Each entry has the following bytestring keys:
77
89
78 name
90 name
79 Name of the compression engine. e.g. ``zstd`` or ``zlib``.
91 Name of the compression engine. e.g. ``zstd`` or ``zlib``.
80
92
81 framingmediatypes
93 framingmediatypes
82 An array of bytestrings defining the supported framing protocol
94 An array of bytestrings defining the supported framing protocol
83 media types. Servers will not accept media types not in this list.
95 media types. Servers will not accept media types not in this list.
84
96
85 pathfilterprefixes
97 pathfilterprefixes
86 (set of bytestring) Matcher prefixes that are recognized when performing
98 (set of bytestring) Matcher prefixes that are recognized when performing
87 path filtering. Specifying a path filter whose type/prefix does not
99 path filtering. Specifying a path filter whose type/prefix does not
88 match one in this set will likely be rejected by the server.
100 match one in this set will likely be rejected by the server.
89
101
90 rawrepoformats
102 rawrepoformats
91 An array of storage formats the repository is using. This set of
103 An array of storage formats the repository is using. This set of
92 requirements can be used to determine whether a client can read a
104 requirements can be used to determine whether a client can read a
93 *raw* copy of file data available.
105 *raw* copy of file data available.
94
106
95 changesetdata
107 changesetdata
96 -------------
108 -------------
97
109
98 Obtain various data related to changesets.
110 Obtain various data related to changesets.
99
111
100 The command accepts the following arguments:
112 The command accepts the following arguments:
101
113
102 noderange
114 noderange
103 (array of arrays of bytestrings) An array of 2 elements, each being an
115 (array of arrays of bytestrings) An array of 2 elements, each being an
104 array of node bytestrings. The first array denotes the changelog revisions
116 array of node bytestrings. The first array denotes the changelog revisions
105 that are already known to the client. The second array denotes the changelog
117 that are already known to the client. The second array denotes the changelog
106 revision DAG heads to fetch. The argument essentially defines a DAG range
118 revision DAG heads to fetch. The argument essentially defines a DAG range
107 bounded by root and head nodes to fetch.
119 bounded by root and head nodes to fetch.
108
120
109 The roots array may be empty. The heads array must be defined.
121 The roots array may be empty. The heads array must be defined.
110
122
111 nodes
123 nodes
112 (array of bytestrings) Changelog revisions to request explicitly.
124 (array of bytestrings) Changelog revisions to request explicitly.
113
125
114 fields
126 fields
115 (set of bytestring) Which data associated with changelog revisions to
127 (set of bytestring) Which data associated with changelog revisions to
116 fetch. The following values are recognized:
128 fetch. The following values are recognized:
117
129
118 bookmarks
130 bookmarks
119 Bookmarks associated with a revision.
131 Bookmarks associated with a revision.
120
132
121 parents
133 parents
122 Parent revisions.
134 Parent revisions.
123
135
124 phase
136 phase
125 The phase state of a revision.
137 The phase state of a revision.
126
138
127 revision
139 revision
128 The raw, revision data for the changelog entry. The hash of this data
140 The raw, revision data for the changelog entry. The hash of this data
129 will match the revision's node value.
141 will match the revision's node value.
130
142
131 The server resolves the set of revisions relevant to the request by taking
143 The server resolves the set of revisions relevant to the request by taking
132 the union of the ``noderange`` and ``nodes`` arguments. At least one of these
144 the union of the ``noderange`` and ``nodes`` arguments. At least one of these
133 arguments must be defined.
145 arguments must be defined.
134
146
135 The response bytestream starts with a CBOR map describing the data that follows.
147 The response bytestream starts with a CBOR map describing the data that follows.
136 This map has the following bytestring keys:
148 This map has the following bytestring keys:
137
149
138 totalitems
150 totalitems
139 (unsigned integer) Total number of changelog revisions whose data is being
151 (unsigned integer) Total number of changelog revisions whose data is being
140 transferred. This maps to the set of revisions in the requested node
152 transferred. This maps to the set of revisions in the requested node
141 range, not the total number of records that follow (see below for why).
153 range, not the total number of records that follow (see below for why).
142
154
143 Following the map header is a series of 0 or more CBOR values. If values
155 Following the map header is a series of 0 or more CBOR values. If values
144 are present, the first value will always be a map describing a single changeset
156 are present, the first value will always be a map describing a single changeset
145 revision. If revision data is requested, the raw revision data (encoded as
157 revision. If revision data is requested, the raw revision data (encoded as
146 a CBOR bytestring) will follow the map describing it. Otherwise, another CBOR
158 a CBOR bytestring) will follow the map describing it. Otherwise, another CBOR
147 map describing the next changeset revision will occur.
159 map describing the next changeset revision will occur.
148
160
149 Each map has the following bytestring keys:
161 Each map has the following bytestring keys:
150
162
151 node
163 node
152 (bytestring) The node value for this revision. This is the SHA-1 hash of
164 (bytestring) The node value for this revision. This is the SHA-1 hash of
153 the raw revision data.
165 the raw revision data.
154
166
155 bookmarks (optional)
167 bookmarks (optional)
156 (array of bytestrings) Bookmarks attached to this revision. Only present
168 (array of bytestrings) Bookmarks attached to this revision. Only present
157 if ``bookmarks`` data is being requested and the revision has bookmarks
169 if ``bookmarks`` data is being requested and the revision has bookmarks
158 attached.
170 attached.
159
171
160 parents (optional)
172 parents (optional)
161 (array of bytestrings) The nodes representing the parent revisions of this
173 (array of bytestrings) The nodes representing the parent revisions of this
162 revision. Only present if ``parents`` data is being requested.
174 revision. Only present if ``parents`` data is being requested.
163
175
164 phase (optional)
176 phase (optional)
165 (bytestring) The phase that a revision is in. Recognized values are
177 (bytestring) The phase that a revision is in. Recognized values are
166 ``secret``, ``draft``, and ``public``. Only present if ``phase`` data
178 ``secret``, ``draft``, and ``public``. Only present if ``phase`` data
167 is being requested.
179 is being requested.
168
180
169 revisionsize (optional)
181 revisionsize (optional)
170 (unsigned integer) Indicates the size of raw revision data that follows this
182 (unsigned integer) Indicates the size of raw revision data that follows this
171 map. The following data contains a serialized form of the changeset data,
183 map. The following data contains a serialized form of the changeset data,
172 including the author, date, commit message, set of changed files, manifest
184 including the author, date, commit message, set of changed files, manifest
173 node, and other metadata.
185 node, and other metadata.
174
186
175 Only present if ``revision`` data was requested and the data follows this
187 Only present if ``revision`` data was requested and the data follows this
176 map.
188 map.
177
189
178 If nodes are requested via ``noderange``, they will be emitted in DAG order,
190 If nodes are requested via ``noderange``, they will be emitted in DAG order,
179 parents always before children.
191 parents always before children.
180
192
181 If nodes are requested via ``nodes``, they will be emitted in requested order.
193 If nodes are requested via ``nodes``, they will be emitted in requested order.
182
194
183 Nodes from ``nodes`` are emitted before nodes from ``noderange``.
195 Nodes from ``nodes`` are emitted before nodes from ``noderange``.
184
196
185 The set of changeset revisions emitted may not match the exact set of
197 The set of changeset revisions emitted may not match the exact set of
186 changesets requested. Furthermore, the set of keys present on each
198 changesets requested. Furthermore, the set of keys present on each
187 map may vary. This is to facilitate emitting changeset updates as well
199 map may vary. This is to facilitate emitting changeset updates as well
188 as new revisions.
200 as new revisions.
189
201
190 For example, if the request wants ``phase`` and ``revision`` data,
202 For example, if the request wants ``phase`` and ``revision`` data,
191 the response may contain entries for each changeset in the common nodes
203 the response may contain entries for each changeset in the common nodes
192 set with the ``phase`` key and without the ``revision`` key in order
204 set with the ``phase`` key and without the ``revision`` key in order
193 to reflect a phase-only update.
205 to reflect a phase-only update.
194
206
195 TODO support different revision selection mechanisms (e.g. non-public, specific
207 TODO support different revision selection mechanisms (e.g. non-public, specific
196 revisions)
208 revisions)
197 TODO support different hash "namespaces" for revisions (e.g. sha-1 versus other)
209 TODO support different hash "namespaces" for revisions (e.g. sha-1 versus other)
198 TODO support emitting obsolescence data
210 TODO support emitting obsolescence data
199 TODO support filtering based on relevant paths (narrow clone)
211 TODO support filtering based on relevant paths (narrow clone)
200 TODO support depth limiting
212 TODO support depth limiting
201 TODO support hgtagsfnodes cache / tags data
213 TODO support hgtagsfnodes cache / tags data
202 TODO support branch heads cache
214 TODO support branch heads cache
203
215
204 filedata
216 filedata
205 --------
217 --------
206
218
207 Obtain various data related to an individual tracked file.
219 Obtain various data related to an individual tracked file.
208
220
209 The command accepts the following arguments:
221 The command accepts the following arguments:
210
222
211 fields
223 fields
212 (set of bytestring) Which data associated with a file to fetch.
224 (set of bytestring) Which data associated with a file to fetch.
213 The following values are recognized:
225 The following values are recognized:
214
226
215 parents
227 parents
216 Parent nodes for the revision.
228 Parent nodes for the revision.
217
229
218 revision
230 revision
219 The raw revision data for a file.
231 The raw revision data for a file.
220
232
221 haveparents
233 haveparents
222 (bool) Whether the client has the parent revisions of all requested
234 (bool) Whether the client has the parent revisions of all requested
223 nodes. If set, the server may emit revision data as deltas against
235 nodes. If set, the server may emit revision data as deltas against
224 any parent revision. If not set, the server MUST only emit deltas for
236 any parent revision. If not set, the server MUST only emit deltas for
225 revisions previously emitted by this command.
237 revisions previously emitted by this command.
226
238
227 False is assumed in the absence of any value.
239 False is assumed in the absence of any value.
228
240
229 nodes
241 nodes
230 (array of bytestrings) File nodes whose data to retrieve.
242 (array of bytestrings) File nodes whose data to retrieve.
231
243
232 path
244 path
233 (bytestring) Path of the tracked file whose data to retrieve.
245 (bytestring) Path of the tracked file whose data to retrieve.
234
246
235 TODO allow specifying revisions via alternate means (such as from
247 TODO allow specifying revisions via alternate means (such as from
236 changeset revisions or ranges)
248 changeset revisions or ranges)
237
249
238 The response bytestream starts with a CBOR map describing the data that
250 The response bytestream starts with a CBOR map describing the data that
239 follows. It has the following bytestream keys:
251 follows. It has the following bytestream keys:
240
252
241 totalitems
253 totalitems
242 (unsigned integer) Total number of file revisions whose data is
254 (unsigned integer) Total number of file revisions whose data is
243 being returned.
255 being returned.
244
256
245 Following the header map is a series of 0 or more CBOR values. The first
257 Following the header map is a series of 0 or more CBOR values. The first
246 value is always a map describing a file revision. If this map has the
258 value is always a map describing a file revision. If this map has the
247 ``deltasize`` or ``revisionsize`` keys, a bytestring containing the delta
259 ``deltasize`` or ``revisionsize`` keys, a bytestring containing the delta
248 or revision, respectively, will immediately follow the map. Otherwise
260 or revision, respectively, will immediately follow the map. Otherwise
249 the next value will be a map describing the next file revision.
261 the next value will be a map describing the next file revision.
250
262
251 Each map has the following bytestring keys:
263 Each map has the following bytestring keys:
252
264
253 node
265 node
254 (bytestring) The node of the file revision whose data is represented.
266 (bytestring) The node of the file revision whose data is represented.
255
267
256 deltabasenode
268 deltabasenode
257 (bytestring) Node of the file revision the following delta is against.
269 (bytestring) Node of the file revision the following delta is against.
258
270
259 Only present if the ``revision`` field is requested and delta data
271 Only present if the ``revision`` field is requested and delta data
260 follows this map.
272 follows this map.
261
273
262 deltasize
274 deltasize
263 (unsigned integer) The size of the delta data that follows this map.
275 (unsigned integer) The size of the delta data that follows this map.
264
276
265 Only present if the ``revision`` field is requested and delta data
277 Only present if the ``revision`` field is requested and delta data
266 follows this map.
278 follows this map.
267
279
268 parents
280 parents
269 (array of bytestring) The nodes of the parents of this file revision.
281 (array of bytestring) The nodes of the parents of this file revision.
270
282
271 Only present if the ``parents`` field is requested.
283 Only present if the ``parents`` field is requested.
272
284
273 revisionsize
285 revisionsize
274 (unsigned integer) The size of the fulltext revision data that follows
286 (unsigned integer) The size of the fulltext revision data that follows
275 this map.
287 this map.
276
288
277 Only present if the ``revision`` field is requested and fulltext revision
289 Only present if the ``revision`` field is requested and fulltext revision
278 data follows this map.
290 data follows this map.
279
291
280 When ``revision`` data is requested, the server chooses to emit either fulltext
292 When ``revision`` data is requested, the server chooses to emit either fulltext
281 revision data or a delta. What the server decides can be inferred by looking
293 revision data or a delta. What the server decides can be inferred by looking
282 for the presence of the ``deltasize`` or ``revisionsize`` keys in the map.
294 for the presence of the ``deltasize`` or ``revisionsize`` keys in the map.
283 Servers MUST NOT define both keys.
295 Servers MUST NOT define both keys.
284
296
285 heads
297 heads
286 -----
298 -----
287
299
288 Obtain DAG heads in the repository.
300 Obtain DAG heads in the repository.
289
301
290 The command accepts the following arguments:
302 The command accepts the following arguments:
291
303
292 publiconly (optional)
304 publiconly (optional)
293 (boolean) If set, operate on the DAG for public phase changesets only.
305 (boolean) If set, operate on the DAG for public phase changesets only.
294 Non-public (i.e. draft) phase DAG heads will not be returned.
306 Non-public (i.e. draft) phase DAG heads will not be returned.
295
307
296 The response is a CBOR array of bytestrings defining changeset nodes
308 The response is a CBOR array of bytestrings defining changeset nodes
297 of DAG heads. The array can be empty if the repository is empty or no
309 of DAG heads. The array can be empty if the repository is empty or no
298 changesets satisfied the request.
310 changesets satisfied the request.
299
311
300 TODO consider exposing phase of heads in response
312 TODO consider exposing phase of heads in response
301
313
302 known
314 known
303 -----
315 -----
304
316
305 Determine whether a series of changeset nodes is known to the server.
317 Determine whether a series of changeset nodes is known to the server.
306
318
307 The command accepts the following arguments:
319 The command accepts the following arguments:
308
320
309 nodes
321 nodes
310 (array of bytestrings) List of changeset nodes whose presence to
322 (array of bytestrings) List of changeset nodes whose presence to
311 query.
323 query.
312
324
313 The response is a bytestring where each byte contains a 0 or 1 for the
325 The response is a bytestring where each byte contains a 0 or 1 for the
314 corresponding requested node at the same index.
326 corresponding requested node at the same index.
315
327
316 TODO use a bit array for even more compact response
328 TODO use a bit array for even more compact response
317
329
318 listkeys
330 listkeys
319 --------
331 --------
320
332
321 List values in a specified ``pushkey`` namespace.
333 List values in a specified ``pushkey`` namespace.
322
334
323 The command receives the following arguments:
335 The command receives the following arguments:
324
336
325 namespace
337 namespace
326 (bytestring) Pushkey namespace to query.
338 (bytestring) Pushkey namespace to query.
327
339
328 The response is a map with bytestring keys and values.
340 The response is a map with bytestring keys and values.
329
341
330 TODO consider using binary to represent nodes in certain pushkey namespaces.
342 TODO consider using binary to represent nodes in certain pushkey namespaces.
331
343
332 lookup
344 lookup
333 ------
345 ------
334
346
335 Try to resolve a value to a changeset revision.
347 Try to resolve a value to a changeset revision.
336
348
337 Unlike ``known`` which operates on changeset nodes, lookup operates on
349 Unlike ``known`` which operates on changeset nodes, lookup operates on
338 node fragments and other names that a user may use.
350 node fragments and other names that a user may use.
339
351
340 The command receives the following arguments:
352 The command receives the following arguments:
341
353
342 key
354 key
343 (bytestring) Value to try to resolve.
355 (bytestring) Value to try to resolve.
344
356
345 On success, returns a bytestring containing the resolved node.
357 On success, returns a bytestring containing the resolved node.
346
358
347 manifestdata
359 manifestdata
348 ------------
360 ------------
349
361
350 Obtain various data related to manifests (which are lists of files in
362 Obtain various data related to manifests (which are lists of files in
351 a revision).
363 a revision).
352
364
353 The command accepts the following arguments:
365 The command accepts the following arguments:
354
366
355 fields
367 fields
356 (set of bytestring) Which data associated with manifests to fetch.
368 (set of bytestring) Which data associated with manifests to fetch.
357 The following values are recognized:
369 The following values are recognized:
358
370
359 parents
371 parents
360 Parent nodes for the manifest.
372 Parent nodes for the manifest.
361
373
362 revision
374 revision
363 The raw revision data for the manifest.
375 The raw revision data for the manifest.
364
376
365 haveparents
377 haveparents
366 (bool) Whether the client has the parent revisions of all requested
378 (bool) Whether the client has the parent revisions of all requested
367 nodes. If set, the server may emit revision data as deltas against
379 nodes. If set, the server may emit revision data as deltas against
368 any parent revision. If not set, the server MUST only emit deltas for
380 any parent revision. If not set, the server MUST only emit deltas for
369 revisions previously emitted by this command.
381 revisions previously emitted by this command.
370
382
371 False is assumed in the absence of any value.
383 False is assumed in the absence of any value.
372
384
373 nodes
385 nodes
374 (array of bytestring) Manifest nodes whose data to retrieve.
386 (array of bytestring) Manifest nodes whose data to retrieve.
375
387
376 tree
388 tree
377 (bytestring) Path to manifest to retrieve. The empty bytestring represents
389 (bytestring) Path to manifest to retrieve. The empty bytestring represents
378 the root manifest. All other values represent directories/trees within
390 the root manifest. All other values represent directories/trees within
379 the repository.
391 the repository.
380
392
381 TODO allow specifying revisions via alternate means (such as from changeset
393 TODO allow specifying revisions via alternate means (such as from changeset
382 revisions or ranges)
394 revisions or ranges)
383 TODO consider recursive expansion of manifests (with path filtering for
395 TODO consider recursive expansion of manifests (with path filtering for
384 narrow use cases)
396 narrow use cases)
385
397
386 The response bytestream starts with a CBOR map describing the data that
398 The response bytestream starts with a CBOR map describing the data that
387 follows. It has the following bytestring keys:
399 follows. It has the following bytestring keys:
388
400
389 totalitems
401 totalitems
390 (unsigned integer) Total number of manifest revisions whose data is
402 (unsigned integer) Total number of manifest revisions whose data is
391 being returned.
403 being returned.
392
404
393 Following the header map is a series of 0 or more CBOR values. The first
405 Following the header map is a series of 0 or more CBOR values. The first
394 value is always a map describing a manifest revision. If this map has the
406 value is always a map describing a manifest revision. If this map has the
395 ``deltasize`` or ``revisionsize`` keys, a bytestring containing the delta
407 ``deltasize`` or ``revisionsize`` keys, a bytestring containing the delta
396 or revision, respectively, will immediately follow the map. Otherwise
408 or revision, respectively, will immediately follow the map. Otherwise
397 the next value will be a map describing the next manifest revision.
409 the next value will be a map describing the next manifest revision.
398
410
399 Each map has the following bytestring keys:
411 Each map has the following bytestring keys:
400
412
401 node
413 node
402 (bytestring) The node of the manifest revision whose data is represented.
414 (bytestring) The node of the manifest revision whose data is represented.
403
415
404 deltabasenode
416 deltabasenode
405 (bytestring) The node that the delta representation of this revision is
417 (bytestring) The node that the delta representation of this revision is
406 computed against. Only present if the ``revision`` field is requested and
418 computed against. Only present if the ``revision`` field is requested and
407 a delta is being emitted.
419 a delta is being emitted.
408
420
409 deltasize
421 deltasize
410 (unsigned integer) The size of the delta data that follows this map.
422 (unsigned integer) The size of the delta data that follows this map.
411 Only present if the ``revision`` field is requested and a delta is
423 Only present if the ``revision`` field is requested and a delta is
412 being emitted.
424 being emitted.
413
425
414 parents
426 parents
415 (array of bytestring) The nodes of the parents of this manifest revision.
427 (array of bytestring) The nodes of the parents of this manifest revision.
416 Only present if the ``parents`` field is requested.
428 Only present if the ``parents`` field is requested.
417
429
418 revisionsize
430 revisionsize
419 (unsigned integer) The size of the fulltext revision data that follows
431 (unsigned integer) The size of the fulltext revision data that follows
420 this map. Only present if the ``revision`` field is requested and a fulltext
432 this map. Only present if the ``revision`` field is requested and a fulltext
421 revision is being emitted.
433 revision is being emitted.
422
434
423 When ``revision`` data is requested, the server chooses to emit either fulltext
435 When ``revision`` data is requested, the server chooses to emit either fulltext
424 revision data or a delta. What the server decides can be inferred by looking
436 revision data or a delta. What the server decides can be inferred by looking
425 for the presence of the ``deltasize`` or ``revisionsize`` keys in the map.
437 for the presence of the ``deltasize`` or ``revisionsize`` keys in the map.
426 Servers MUST NOT define both keys.
438 Servers MUST NOT define both keys.
427
439
428 pushkey
440 pushkey
429 -------
441 -------
430
442
431 Set a value using the ``pushkey`` protocol.
443 Set a value using the ``pushkey`` protocol.
432
444
433 The command receives the following arguments:
445 The command receives the following arguments:
434
446
435 namespace
447 namespace
436 (bytestring) Pushkey namespace to operate on.
448 (bytestring) Pushkey namespace to operate on.
437 key
449 key
438 (bytestring) The pushkey key to set.
450 (bytestring) The pushkey key to set.
439 old
451 old
440 (bytestring) Old value for this key.
452 (bytestring) Old value for this key.
441 new
453 new
442 (bytestring) New value for this key.
454 (bytestring) New value for this key.
443
455
444 TODO consider using binary to represent nodes is certain pushkey namespaces.
456 TODO consider using binary to represent nodes is certain pushkey namespaces.
445 TODO better define response type and meaning.
457 TODO better define response type and meaning.
@@ -1,1060 +1,1071 b''
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 hex,
13 hex,
14 nullid,
14 nullid,
15 nullrev,
15 nullrev,
16 )
16 )
17 from . import (
17 from . import (
18 changegroup,
18 changegroup,
19 dagop,
19 dagop,
20 discovery,
20 discovery,
21 encoding,
21 encoding,
22 error,
22 error,
23 narrowspec,
23 narrowspec,
24 pycompat,
24 pycompat,
25 streamclone,
25 streamclone,
26 util,
26 util,
27 wireprotoframing,
27 wireprotoframing,
28 wireprototypes,
28 wireprototypes,
29 )
29 )
30 from .utils import (
30 from .utils import (
31 interfaceutil,
31 interfaceutil,
32 )
32 )
33
33
34 FRAMINGTYPE = b'application/mercurial-exp-framing-0005'
34 FRAMINGTYPE = b'application/mercurial-exp-framing-0005'
35
35
36 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
36 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
37
37
38 COMMANDS = wireprototypes.commanddict()
38 COMMANDS = wireprototypes.commanddict()
39
39
40 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
40 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
41 from .hgweb import common as hgwebcommon
41 from .hgweb import common as hgwebcommon
42
42
43 # URL space looks like: <permissions>/<command>, where <permission> can
43 # URL space looks like: <permissions>/<command>, where <permission> can
44 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
44 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
45
45
46 # Root URL does nothing meaningful... yet.
46 # Root URL does nothing meaningful... yet.
47 if not urlparts:
47 if not urlparts:
48 res.status = b'200 OK'
48 res.status = b'200 OK'
49 res.headers[b'Content-Type'] = b'text/plain'
49 res.headers[b'Content-Type'] = b'text/plain'
50 res.setbodybytes(_('HTTP version 2 API handler'))
50 res.setbodybytes(_('HTTP version 2 API handler'))
51 return
51 return
52
52
53 if len(urlparts) == 1:
53 if len(urlparts) == 1:
54 res.status = b'404 Not Found'
54 res.status = b'404 Not Found'
55 res.headers[b'Content-Type'] = b'text/plain'
55 res.headers[b'Content-Type'] = b'text/plain'
56 res.setbodybytes(_('do not know how to process %s\n') %
56 res.setbodybytes(_('do not know how to process %s\n') %
57 req.dispatchpath)
57 req.dispatchpath)
58 return
58 return
59
59
60 permission, command = urlparts[0:2]
60 permission, command = urlparts[0:2]
61
61
62 if permission not in (b'ro', b'rw'):
62 if permission not in (b'ro', b'rw'):
63 res.status = b'404 Not Found'
63 res.status = b'404 Not Found'
64 res.headers[b'Content-Type'] = b'text/plain'
64 res.headers[b'Content-Type'] = b'text/plain'
65 res.setbodybytes(_('unknown permission: %s') % permission)
65 res.setbodybytes(_('unknown permission: %s') % permission)
66 return
66 return
67
67
68 if req.method != 'POST':
68 if req.method != 'POST':
69 res.status = b'405 Method Not Allowed'
69 res.status = b'405 Method Not Allowed'
70 res.headers[b'Allow'] = b'POST'
70 res.headers[b'Allow'] = b'POST'
71 res.setbodybytes(_('commands require POST requests'))
71 res.setbodybytes(_('commands require POST requests'))
72 return
72 return
73
73
74 # At some point we'll want to use our own API instead of recycling the
74 # At some point we'll want to use our own API instead of recycling the
75 # behavior of version 1 of the wire protocol...
75 # behavior of version 1 of the wire protocol...
76 # TODO return reasonable responses - not responses that overload the
76 # TODO return reasonable responses - not responses that overload the
77 # HTTP status line message for error reporting.
77 # HTTP status line message for error reporting.
78 try:
78 try:
79 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
79 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
80 except hgwebcommon.ErrorResponse as e:
80 except hgwebcommon.ErrorResponse as e:
81 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
81 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
82 for k, v in e.headers:
82 for k, v in e.headers:
83 res.headers[k] = v
83 res.headers[k] = v
84 res.setbodybytes('permission denied')
84 res.setbodybytes('permission denied')
85 return
85 return
86
86
87 # We have a special endpoint to reflect the request back at the client.
87 # We have a special endpoint to reflect the request back at the client.
88 if command == b'debugreflect':
88 if command == b'debugreflect':
89 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
89 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
90 return
90 return
91
91
92 # Extra commands that we handle that aren't really wire protocol
92 # Extra commands that we handle that aren't really wire protocol
93 # commands. Think extra hard before making this hackery available to
93 # commands. Think extra hard before making this hackery available to
94 # extension.
94 # extension.
95 extracommands = {'multirequest'}
95 extracommands = {'multirequest'}
96
96
97 if command not in COMMANDS and command not in extracommands:
97 if command not in COMMANDS and command not in extracommands:
98 res.status = b'404 Not Found'
98 res.status = b'404 Not Found'
99 res.headers[b'Content-Type'] = b'text/plain'
99 res.headers[b'Content-Type'] = b'text/plain'
100 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
100 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
101 return
101 return
102
102
103 repo = rctx.repo
103 repo = rctx.repo
104 ui = repo.ui
104 ui = repo.ui
105
105
106 proto = httpv2protocolhandler(req, ui)
106 proto = httpv2protocolhandler(req, ui)
107
107
108 if (not COMMANDS.commandavailable(command, proto)
108 if (not COMMANDS.commandavailable(command, proto)
109 and command not in extracommands):
109 and command not in extracommands):
110 res.status = b'404 Not Found'
110 res.status = b'404 Not Found'
111 res.headers[b'Content-Type'] = b'text/plain'
111 res.headers[b'Content-Type'] = b'text/plain'
112 res.setbodybytes(_('invalid wire protocol command: %s') % command)
112 res.setbodybytes(_('invalid wire protocol command: %s') % command)
113 return
113 return
114
114
115 # TODO consider cases where proxies may add additional Accept headers.
115 # TODO consider cases where proxies may add additional Accept headers.
116 if req.headers.get(b'Accept') != FRAMINGTYPE:
116 if req.headers.get(b'Accept') != FRAMINGTYPE:
117 res.status = b'406 Not Acceptable'
117 res.status = b'406 Not Acceptable'
118 res.headers[b'Content-Type'] = b'text/plain'
118 res.headers[b'Content-Type'] = b'text/plain'
119 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
119 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
120 % FRAMINGTYPE)
120 % FRAMINGTYPE)
121 return
121 return
122
122
123 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
123 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
124 res.status = b'415 Unsupported Media Type'
124 res.status = b'415 Unsupported Media Type'
125 # TODO we should send a response with appropriate media type,
125 # TODO we should send a response with appropriate media type,
126 # since client does Accept it.
126 # since client does Accept it.
127 res.headers[b'Content-Type'] = b'text/plain'
127 res.headers[b'Content-Type'] = b'text/plain'
128 res.setbodybytes(_('client MUST send Content-Type header with '
128 res.setbodybytes(_('client MUST send Content-Type header with '
129 'value: %s\n') % FRAMINGTYPE)
129 'value: %s\n') % FRAMINGTYPE)
130 return
130 return
131
131
132 _processhttpv2request(ui, repo, req, res, permission, command, proto)
132 _processhttpv2request(ui, repo, req, res, permission, command, proto)
133
133
134 def _processhttpv2reflectrequest(ui, repo, req, res):
134 def _processhttpv2reflectrequest(ui, repo, req, res):
135 """Reads unified frame protocol request and dumps out state to client.
135 """Reads unified frame protocol request and dumps out state to client.
136
136
137 This special endpoint can be used to help debug the wire protocol.
137 This special endpoint can be used to help debug the wire protocol.
138
138
139 Instead of routing the request through the normal dispatch mechanism,
139 Instead of routing the request through the normal dispatch mechanism,
140 we instead read all frames, decode them, and feed them into our state
140 we instead read all frames, decode them, and feed them into our state
141 tracker. We then dump the log of all that activity back out to the
141 tracker. We then dump the log of all that activity back out to the
142 client.
142 client.
143 """
143 """
144 import json
144 import json
145
145
146 # Reflection APIs have a history of being abused, accidentally disclosing
146 # Reflection APIs have a history of being abused, accidentally disclosing
147 # sensitive data, etc. So we have a config knob.
147 # sensitive data, etc. So we have a config knob.
148 if not ui.configbool('experimental', 'web.api.debugreflect'):
148 if not ui.configbool('experimental', 'web.api.debugreflect'):
149 res.status = b'404 Not Found'
149 res.status = b'404 Not Found'
150 res.headers[b'Content-Type'] = b'text/plain'
150 res.headers[b'Content-Type'] = b'text/plain'
151 res.setbodybytes(_('debugreflect service not available'))
151 res.setbodybytes(_('debugreflect service not available'))
152 return
152 return
153
153
154 # We assume we have a unified framing protocol request body.
154 # We assume we have a unified framing protocol request body.
155
155
156 reactor = wireprotoframing.serverreactor()
156 reactor = wireprotoframing.serverreactor()
157 states = []
157 states = []
158
158
159 while True:
159 while True:
160 frame = wireprotoframing.readframe(req.bodyfh)
160 frame = wireprotoframing.readframe(req.bodyfh)
161
161
162 if not frame:
162 if not frame:
163 states.append(b'received: <no frame>')
163 states.append(b'received: <no frame>')
164 break
164 break
165
165
166 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
166 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
167 frame.requestid,
167 frame.requestid,
168 frame.payload))
168 frame.payload))
169
169
170 action, meta = reactor.onframerecv(frame)
170 action, meta = reactor.onframerecv(frame)
171 states.append(json.dumps((action, meta), sort_keys=True,
171 states.append(json.dumps((action, meta), sort_keys=True,
172 separators=(', ', ': ')))
172 separators=(', ', ': ')))
173
173
174 action, meta = reactor.oninputeof()
174 action, meta = reactor.oninputeof()
175 meta['action'] = action
175 meta['action'] = action
176 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
176 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
177
177
178 res.status = b'200 OK'
178 res.status = b'200 OK'
179 res.headers[b'Content-Type'] = b'text/plain'
179 res.headers[b'Content-Type'] = b'text/plain'
180 res.setbodybytes(b'\n'.join(states))
180 res.setbodybytes(b'\n'.join(states))
181
181
182 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
182 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
183 """Post-validation handler for HTTPv2 requests.
183 """Post-validation handler for HTTPv2 requests.
184
184
185 Called when the HTTP request contains unified frame-based protocol
185 Called when the HTTP request contains unified frame-based protocol
186 frames for evaluation.
186 frames for evaluation.
187 """
187 """
188 # TODO Some HTTP clients are full duplex and can receive data before
188 # TODO Some HTTP clients are full duplex and can receive data before
189 # the entire request is transmitted. Figure out a way to indicate support
189 # the entire request is transmitted. Figure out a way to indicate support
190 # for that so we can opt into full duplex mode.
190 # for that so we can opt into full duplex mode.
191 reactor = wireprotoframing.serverreactor(deferoutput=True)
191 reactor = wireprotoframing.serverreactor(deferoutput=True)
192 seencommand = False
192 seencommand = False
193
193
194 outstream = reactor.makeoutputstream()
194 outstream = reactor.makeoutputstream()
195
195
196 while True:
196 while True:
197 frame = wireprotoframing.readframe(req.bodyfh)
197 frame = wireprotoframing.readframe(req.bodyfh)
198 if not frame:
198 if not frame:
199 break
199 break
200
200
201 action, meta = reactor.onframerecv(frame)
201 action, meta = reactor.onframerecv(frame)
202
202
203 if action == 'wantframe':
203 if action == 'wantframe':
204 # Need more data before we can do anything.
204 # Need more data before we can do anything.
205 continue
205 continue
206 elif action == 'runcommand':
206 elif action == 'runcommand':
207 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
207 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
208 reqcommand, reactor, outstream,
208 reqcommand, reactor, outstream,
209 meta, issubsequent=seencommand)
209 meta, issubsequent=seencommand)
210
210
211 if sentoutput:
211 if sentoutput:
212 return
212 return
213
213
214 seencommand = True
214 seencommand = True
215
215
216 elif action == 'error':
216 elif action == 'error':
217 # TODO define proper error mechanism.
217 # TODO define proper error mechanism.
218 res.status = b'200 OK'
218 res.status = b'200 OK'
219 res.headers[b'Content-Type'] = b'text/plain'
219 res.headers[b'Content-Type'] = b'text/plain'
220 res.setbodybytes(meta['message'] + b'\n')
220 res.setbodybytes(meta['message'] + b'\n')
221 return
221 return
222 else:
222 else:
223 raise error.ProgrammingError(
223 raise error.ProgrammingError(
224 'unhandled action from frame processor: %s' % action)
224 'unhandled action from frame processor: %s' % action)
225
225
226 action, meta = reactor.oninputeof()
226 action, meta = reactor.oninputeof()
227 if action == 'sendframes':
227 if action == 'sendframes':
228 # We assume we haven't started sending the response yet. If we're
228 # We assume we haven't started sending the response yet. If we're
229 # wrong, the response type will raise an exception.
229 # wrong, the response type will raise an exception.
230 res.status = b'200 OK'
230 res.status = b'200 OK'
231 res.headers[b'Content-Type'] = FRAMINGTYPE
231 res.headers[b'Content-Type'] = FRAMINGTYPE
232 res.setbodygen(meta['framegen'])
232 res.setbodygen(meta['framegen'])
233 elif action == 'noop':
233 elif action == 'noop':
234 pass
234 pass
235 else:
235 else:
236 raise error.ProgrammingError('unhandled action from frame processor: %s'
236 raise error.ProgrammingError('unhandled action from frame processor: %s'
237 % action)
237 % action)
238
238
239 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
239 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
240 outstream, command, issubsequent):
240 outstream, command, issubsequent):
241 """Dispatch a wire protocol command made from HTTPv2 requests.
241 """Dispatch a wire protocol command made from HTTPv2 requests.
242
242
243 The authenticated permission (``authedperm``) along with the original
243 The authenticated permission (``authedperm``) along with the original
244 command from the URL (``reqcommand``) are passed in.
244 command from the URL (``reqcommand``) are passed in.
245 """
245 """
246 # We already validated that the session has permissions to perform the
246 # We already validated that the session has permissions to perform the
247 # actions in ``authedperm``. In the unified frame protocol, the canonical
247 # actions in ``authedperm``. In the unified frame protocol, the canonical
248 # command to run is expressed in a frame. However, the URL also requested
248 # command to run is expressed in a frame. However, the URL also requested
249 # to run a specific command. We need to be careful that the command we
249 # to run a specific command. We need to be careful that the command we
250 # run doesn't have permissions requirements greater than what was granted
250 # run doesn't have permissions requirements greater than what was granted
251 # by ``authedperm``.
251 # by ``authedperm``.
252 #
252 #
253 # Our rule for this is we only allow one command per HTTP request and
253 # Our rule for this is we only allow one command per HTTP request and
254 # that command must match the command in the URL. However, we make
254 # that command must match the command in the URL. However, we make
255 # an exception for the ``multirequest`` URL. This URL is allowed to
255 # an exception for the ``multirequest`` URL. This URL is allowed to
256 # execute multiple commands. We double check permissions of each command
256 # execute multiple commands. We double check permissions of each command
257 # as it is invoked to ensure there is no privilege escalation.
257 # as it is invoked to ensure there is no privilege escalation.
258 # TODO consider allowing multiple commands to regular command URLs
258 # TODO consider allowing multiple commands to regular command URLs
259 # iff each command is the same.
259 # iff each command is the same.
260
260
261 proto = httpv2protocolhandler(req, ui, args=command['args'])
261 proto = httpv2protocolhandler(req, ui, args=command['args'])
262
262
263 if reqcommand == b'multirequest':
263 if reqcommand == b'multirequest':
264 if not COMMANDS.commandavailable(command['command'], proto):
264 if not COMMANDS.commandavailable(command['command'], proto):
265 # TODO proper error mechanism
265 # TODO proper error mechanism
266 res.status = b'200 OK'
266 res.status = b'200 OK'
267 res.headers[b'Content-Type'] = b'text/plain'
267 res.headers[b'Content-Type'] = b'text/plain'
268 res.setbodybytes(_('wire protocol command not available: %s') %
268 res.setbodybytes(_('wire protocol command not available: %s') %
269 command['command'])
269 command['command'])
270 return True
270 return True
271
271
272 # TODO don't use assert here, since it may be elided by -O.
272 # TODO don't use assert here, since it may be elided by -O.
273 assert authedperm in (b'ro', b'rw')
273 assert authedperm in (b'ro', b'rw')
274 wirecommand = COMMANDS[command['command']]
274 wirecommand = COMMANDS[command['command']]
275 assert wirecommand.permission in ('push', 'pull')
275 assert wirecommand.permission in ('push', 'pull')
276
276
277 if authedperm == b'ro' and wirecommand.permission != 'pull':
277 if authedperm == b'ro' and wirecommand.permission != 'pull':
278 # TODO proper error mechanism
278 # TODO proper error mechanism
279 res.status = b'403 Forbidden'
279 res.status = b'403 Forbidden'
280 res.headers[b'Content-Type'] = b'text/plain'
280 res.headers[b'Content-Type'] = b'text/plain'
281 res.setbodybytes(_('insufficient permissions to execute '
281 res.setbodybytes(_('insufficient permissions to execute '
282 'command: %s') % command['command'])
282 'command: %s') % command['command'])
283 return True
283 return True
284
284
285 # TODO should we also call checkperm() here? Maybe not if we're going
285 # TODO should we also call checkperm() here? Maybe not if we're going
286 # to overhaul that API. The granted scope from the URL check should
286 # to overhaul that API. The granted scope from the URL check should
287 # be good enough.
287 # be good enough.
288
288
289 else:
289 else:
290 # Don't allow multiple commands outside of ``multirequest`` URL.
290 # Don't allow multiple commands outside of ``multirequest`` URL.
291 if issubsequent:
291 if issubsequent:
292 # TODO proper error mechanism
292 # TODO proper error mechanism
293 res.status = b'200 OK'
293 res.status = b'200 OK'
294 res.headers[b'Content-Type'] = b'text/plain'
294 res.headers[b'Content-Type'] = b'text/plain'
295 res.setbodybytes(_('multiple commands cannot be issued to this '
295 res.setbodybytes(_('multiple commands cannot be issued to this '
296 'URL'))
296 'URL'))
297 return True
297 return True
298
298
299 if reqcommand != command['command']:
299 if reqcommand != command['command']:
300 # TODO define proper error mechanism
300 # TODO define proper error mechanism
301 res.status = b'200 OK'
301 res.status = b'200 OK'
302 res.headers[b'Content-Type'] = b'text/plain'
302 res.headers[b'Content-Type'] = b'text/plain'
303 res.setbodybytes(_('command in frame must match command in URL'))
303 res.setbodybytes(_('command in frame must match command in URL'))
304 return True
304 return True
305
305
306 res.status = b'200 OK'
306 res.status = b'200 OK'
307 res.headers[b'Content-Type'] = FRAMINGTYPE
307 res.headers[b'Content-Type'] = FRAMINGTYPE
308
308
309 try:
309 try:
310 objs = dispatch(repo, proto, command['command'])
310 objs = dispatch(repo, proto, command['command'])
311
311
312 action, meta = reactor.oncommandresponsereadyobjects(
312 action, meta = reactor.oncommandresponsereadyobjects(
313 outstream, command['requestid'], objs)
313 outstream, command['requestid'], objs)
314
314
315 except error.WireprotoCommandError as e:
315 except error.WireprotoCommandError as e:
316 action, meta = reactor.oncommanderror(
316 action, meta = reactor.oncommanderror(
317 outstream, command['requestid'], e.message, e.messageargs)
317 outstream, command['requestid'], e.message, e.messageargs)
318
318
319 except Exception as e:
319 except Exception as e:
320 action, meta = reactor.onservererror(
320 action, meta = reactor.onservererror(
321 outstream, command['requestid'],
321 outstream, command['requestid'],
322 _('exception when invoking command: %s') % e)
322 _('exception when invoking command: %s') % e)
323
323
324 if action == 'sendframes':
324 if action == 'sendframes':
325 res.setbodygen(meta['framegen'])
325 res.setbodygen(meta['framegen'])
326 return True
326 return True
327 elif action == 'noop':
327 elif action == 'noop':
328 return False
328 return False
329 else:
329 else:
330 raise error.ProgrammingError('unhandled event from reactor: %s' %
330 raise error.ProgrammingError('unhandled event from reactor: %s' %
331 action)
331 action)
332
332
333 def getdispatchrepo(repo, proto, command):
333 def getdispatchrepo(repo, proto, command):
334 return repo.filtered('served')
334 return repo.filtered('served')
335
335
336 def dispatch(repo, proto, command):
336 def dispatch(repo, proto, command):
337 repo = getdispatchrepo(repo, proto, command)
337 repo = getdispatchrepo(repo, proto, command)
338
338
339 func, spec = COMMANDS[command]
339 func, spec = COMMANDS[command]
340 args = proto.getargs(spec)
340 args = proto.getargs(spec)
341
341
342 return func(repo, proto, **args)
342 return func(repo, proto, **args)
343
343
344 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
344 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
345 class httpv2protocolhandler(object):
345 class httpv2protocolhandler(object):
346 def __init__(self, req, ui, args=None):
346 def __init__(self, req, ui, args=None):
347 self._req = req
347 self._req = req
348 self._ui = ui
348 self._ui = ui
349 self._args = args
349 self._args = args
350
350
351 @property
351 @property
352 def name(self):
352 def name(self):
353 return HTTP_WIREPROTO_V2
353 return HTTP_WIREPROTO_V2
354
354
355 def getargs(self, args):
355 def getargs(self, args):
356 # First look for args that were passed but aren't registered on this
356 # First look for args that were passed but aren't registered on this
357 # command.
357 # command.
358 extra = set(self._args) - set(args)
358 extra = set(self._args) - set(args)
359 if extra:
359 if extra:
360 raise error.WireprotoCommandError(
360 raise error.WireprotoCommandError(
361 'unsupported argument to command: %s' %
361 'unsupported argument to command: %s' %
362 ', '.join(sorted(extra)))
362 ', '.join(sorted(extra)))
363
363
364 # And look for required arguments that are missing.
364 # And look for required arguments that are missing.
365 missing = {a for a in args if args[a]['required']} - set(self._args)
365 missing = {a for a in args if args[a]['required']} - set(self._args)
366
366
367 if missing:
367 if missing:
368 raise error.WireprotoCommandError(
368 raise error.WireprotoCommandError(
369 'missing required arguments: %s' % ', '.join(sorted(missing)))
369 'missing required arguments: %s' % ', '.join(sorted(missing)))
370
370
371 # Now derive the arguments to pass to the command, taking into
371 # Now derive the arguments to pass to the command, taking into
372 # account the arguments specified by the client.
372 # account the arguments specified by the client.
373 data = {}
373 data = {}
374 for k, meta in sorted(args.items()):
374 for k, meta in sorted(args.items()):
375 # This argument wasn't passed by the client.
375 # This argument wasn't passed by the client.
376 if k not in self._args:
376 if k not in self._args:
377 data[k] = meta['default']()
377 data[k] = meta['default']()
378 continue
378 continue
379
379
380 v = self._args[k]
380 v = self._args[k]
381
381
382 # Sets may be expressed as lists. Silently normalize.
382 # Sets may be expressed as lists. Silently normalize.
383 if meta['type'] == 'set' and isinstance(v, list):
383 if meta['type'] == 'set' and isinstance(v, list):
384 v = set(v)
384 v = set(v)
385
385
386 # TODO consider more/stronger type validation.
386 # TODO consider more/stronger type validation.
387
387
388 data[k] = v
388 data[k] = v
389
389
390 return data
390 return data
391
391
392 def getprotocaps(self):
392 def getprotocaps(self):
393 # Protocol capabilities are currently not implemented for HTTP V2.
393 # Protocol capabilities are currently not implemented for HTTP V2.
394 return set()
394 return set()
395
395
396 def getpayload(self):
396 def getpayload(self):
397 raise NotImplementedError
397 raise NotImplementedError
398
398
399 @contextlib.contextmanager
399 @contextlib.contextmanager
400 def mayberedirectstdio(self):
400 def mayberedirectstdio(self):
401 raise NotImplementedError
401 raise NotImplementedError
402
402
403 def client(self):
403 def client(self):
404 raise NotImplementedError
404 raise NotImplementedError
405
405
406 def addcapabilities(self, repo, caps):
406 def addcapabilities(self, repo, caps):
407 return caps
407 return caps
408
408
409 def checkperm(self, perm):
409 def checkperm(self, perm):
410 raise NotImplementedError
410 raise NotImplementedError
411
411
412 def httpv2apidescriptor(req, repo):
412 def httpv2apidescriptor(req, repo):
413 proto = httpv2protocolhandler(req, repo.ui)
413 proto = httpv2protocolhandler(req, repo.ui)
414
414
415 return _capabilitiesv2(repo, proto)
415 return _capabilitiesv2(repo, proto)
416
416
417 def _capabilitiesv2(repo, proto):
417 def _capabilitiesv2(repo, proto):
418 """Obtain the set of capabilities for version 2 transports.
418 """Obtain the set of capabilities for version 2 transports.
419
419
420 These capabilities are distinct from the capabilities for version 1
420 These capabilities are distinct from the capabilities for version 1
421 transports.
421 transports.
422 """
422 """
423 compression = []
423 compression = []
424 for engine in wireprototypes.supportedcompengines(repo.ui, util.SERVERROLE):
424 for engine in wireprototypes.supportedcompengines(repo.ui, util.SERVERROLE):
425 compression.append({
425 compression.append({
426 b'name': engine.wireprotosupport().name,
426 b'name': engine.wireprotosupport().name,
427 })
427 })
428
428
429 caps = {
429 caps = {
430 'commands': {},
430 'commands': {},
431 'compression': compression,
431 'compression': compression,
432 'framingmediatypes': [FRAMINGTYPE],
432 'framingmediatypes': [FRAMINGTYPE],
433 'pathfilterprefixes': set(narrowspec.VALID_PREFIXES),
433 'pathfilterprefixes': set(narrowspec.VALID_PREFIXES),
434 }
434 }
435
435
436 # TODO expose available changesetdata fields.
436 # TODO expose available changesetdata fields.
437
437
438 for command, entry in COMMANDS.items():
438 for command, entry in COMMANDS.items():
439 args = {arg: meta['example'] for arg, meta in entry.args.items()}
439 args = {}
440
441 for arg, meta in entry.args.items():
442 args[arg] = {
443 # TODO should this be a normalized type using CBOR's
444 # terminology?
445 b'type': meta['type'],
446 b'required': meta['required'],
447 }
448
449 if not meta['required']:
450 args[arg][b'default'] = meta['default']()
440
451
441 caps['commands'][command] = {
452 caps['commands'][command] = {
442 'args': args,
453 'args': args,
443 'permissions': [entry.permission],
454 'permissions': [entry.permission],
444 }
455 }
445
456
446 if streamclone.allowservergeneration(repo):
457 if streamclone.allowservergeneration(repo):
447 caps['rawrepoformats'] = sorted(repo.requirements &
458 caps['rawrepoformats'] = sorted(repo.requirements &
448 repo.supportedformats)
459 repo.supportedformats)
449
460
450 return proto.addcapabilities(repo, caps)
461 return proto.addcapabilities(repo, caps)
451
462
452 def builddeltarequests(store, nodes, haveparents):
463 def builddeltarequests(store, nodes, haveparents):
453 """Build a series of revision delta requests against a backend store.
464 """Build a series of revision delta requests against a backend store.
454
465
455 Returns a list of revision numbers in the order they should be sent
466 Returns a list of revision numbers in the order they should be sent
456 and a list of ``irevisiondeltarequest`` instances to be made against
467 and a list of ``irevisiondeltarequest`` instances to be made against
457 the backend store.
468 the backend store.
458 """
469 """
459 # We sort and send nodes in DAG order because this is optimal for
470 # We sort and send nodes in DAG order because this is optimal for
460 # storage emission.
471 # storage emission.
461 # TODO we may want a better storage API here - one where we can throw
472 # TODO we may want a better storage API here - one where we can throw
462 # a list of nodes and delta preconditions over a figurative wall and
473 # a list of nodes and delta preconditions over a figurative wall and
463 # have the storage backend figure it out for us.
474 # have the storage backend figure it out for us.
464 revs = dagop.linearize({store.rev(n) for n in nodes}, store.parentrevs)
475 revs = dagop.linearize({store.rev(n) for n in nodes}, store.parentrevs)
465
476
466 requests = []
477 requests = []
467 seenrevs = set()
478 seenrevs = set()
468
479
469 for rev in revs:
480 for rev in revs:
470 node = store.node(rev)
481 node = store.node(rev)
471 parentnodes = store.parents(node)
482 parentnodes = store.parents(node)
472 parentrevs = [store.rev(n) for n in parentnodes]
483 parentrevs = [store.rev(n) for n in parentnodes]
473 deltabaserev = store.deltaparent(rev)
484 deltabaserev = store.deltaparent(rev)
474 deltabasenode = store.node(deltabaserev)
485 deltabasenode = store.node(deltabaserev)
475
486
476 # The choice of whether to send a fulltext revision or a delta and
487 # The choice of whether to send a fulltext revision or a delta and
477 # what delta to send is governed by a few factors.
488 # what delta to send is governed by a few factors.
478 #
489 #
479 # To send a delta, we need to ensure the receiver is capable of
490 # To send a delta, we need to ensure the receiver is capable of
480 # decoding it. And that requires the receiver to have the base
491 # decoding it. And that requires the receiver to have the base
481 # revision the delta is against.
492 # revision the delta is against.
482 #
493 #
483 # We can only guarantee the receiver has the base revision if
494 # We can only guarantee the receiver has the base revision if
484 # a) we've already sent the revision as part of this group
495 # a) we've already sent the revision as part of this group
485 # b) the receiver has indicated they already have the revision.
496 # b) the receiver has indicated they already have the revision.
486 # And the mechanism for "b" is the client indicating they have
497 # And the mechanism for "b" is the client indicating they have
487 # parent revisions. So this means we can only send the delta if
498 # parent revisions. So this means we can only send the delta if
488 # it is sent before or it is against a delta and the receiver says
499 # it is sent before or it is against a delta and the receiver says
489 # they have a parent.
500 # they have a parent.
490
501
491 # We can send storage delta if it is against a revision we've sent
502 # We can send storage delta if it is against a revision we've sent
492 # in this group.
503 # in this group.
493 if deltabaserev != nullrev and deltabaserev in seenrevs:
504 if deltabaserev != nullrev and deltabaserev in seenrevs:
494 basenode = deltabasenode
505 basenode = deltabasenode
495
506
496 # We can send storage delta if it is against a parent revision and
507 # We can send storage delta if it is against a parent revision and
497 # the receiver indicates they have the parents.
508 # the receiver indicates they have the parents.
498 elif (deltabaserev != nullrev and deltabaserev in parentrevs
509 elif (deltabaserev != nullrev and deltabaserev in parentrevs
499 and haveparents):
510 and haveparents):
500 basenode = deltabasenode
511 basenode = deltabasenode
501
512
502 # Otherwise the storage delta isn't appropriate. Fall back to
513 # Otherwise the storage delta isn't appropriate. Fall back to
503 # using another delta, if possible.
514 # using another delta, if possible.
504
515
505 # Use p1 if we've emitted it or receiver says they have it.
516 # Use p1 if we've emitted it or receiver says they have it.
506 elif parentrevs[0] != nullrev and (
517 elif parentrevs[0] != nullrev and (
507 parentrevs[0] in seenrevs or haveparents):
518 parentrevs[0] in seenrevs or haveparents):
508 basenode = parentnodes[0]
519 basenode = parentnodes[0]
509
520
510 # Use p2 if we've emitted it or receiver says they have it.
521 # Use p2 if we've emitted it or receiver says they have it.
511 elif parentrevs[1] != nullrev and (
522 elif parentrevs[1] != nullrev and (
512 parentrevs[1] in seenrevs or haveparents):
523 parentrevs[1] in seenrevs or haveparents):
513 basenode = parentnodes[1]
524 basenode = parentnodes[1]
514
525
515 # Nothing appropriate to delta against. Send the full revision.
526 # Nothing appropriate to delta against. Send the full revision.
516 else:
527 else:
517 basenode = nullid
528 basenode = nullid
518
529
519 requests.append(changegroup.revisiondeltarequest(
530 requests.append(changegroup.revisiondeltarequest(
520 node=node,
531 node=node,
521 p1node=parentnodes[0],
532 p1node=parentnodes[0],
522 p2node=parentnodes[1],
533 p2node=parentnodes[1],
523 # Receiver deals with linknode resolution.
534 # Receiver deals with linknode resolution.
524 linknode=nullid,
535 linknode=nullid,
525 basenode=basenode,
536 basenode=basenode,
526 ))
537 ))
527
538
528 seenrevs.add(rev)
539 seenrevs.add(rev)
529
540
530 return revs, requests
541 return revs, requests
531
542
532 def wireprotocommand(name, args=None, permission='push'):
543 def wireprotocommand(name, args=None, permission='push'):
533 """Decorator to declare a wire protocol command.
544 """Decorator to declare a wire protocol command.
534
545
535 ``name`` is the name of the wire protocol command being provided.
546 ``name`` is the name of the wire protocol command being provided.
536
547
537 ``args`` is a dict defining arguments accepted by the command. Keys are
548 ``args`` is a dict defining arguments accepted by the command. Keys are
538 the argument name. Values are dicts with the following keys:
549 the argument name. Values are dicts with the following keys:
539
550
540 ``type``
551 ``type``
541 The argument data type. Must be one of the following string
552 The argument data type. Must be one of the following string
542 literals: ``bytes``, ``int``, ``list``, ``dict``, ``set``,
553 literals: ``bytes``, ``int``, ``list``, ``dict``, ``set``,
543 or ``bool``.
554 or ``bool``.
544
555
545 ``default``
556 ``default``
546 A callable returning the default value for this argument. If not
557 A callable returning the default value for this argument. If not
547 specified, ``None`` will be the default value.
558 specified, ``None`` will be the default value.
548
559
549 ``required``
560 ``required``
550 Bool indicating whether the argument is required.
561 Bool indicating whether the argument is required.
551
562
552 ``example``
563 ``example``
553 An example value for this argument.
564 An example value for this argument.
554
565
555 ``permission`` defines the permission type needed to run this command.
566 ``permission`` defines the permission type needed to run this command.
556 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
567 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
557 respectively. Default is to assume command requires ``push`` permissions
568 respectively. Default is to assume command requires ``push`` permissions
558 because otherwise commands not declaring their permissions could modify
569 because otherwise commands not declaring their permissions could modify
559 a repository that is supposed to be read-only.
570 a repository that is supposed to be read-only.
560
571
561 Wire protocol commands are generators of objects to be serialized and
572 Wire protocol commands are generators of objects to be serialized and
562 sent to the client.
573 sent to the client.
563
574
564 If a command raises an uncaught exception, this will be translated into
575 If a command raises an uncaught exception, this will be translated into
565 a command error.
576 a command error.
566 """
577 """
567 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
578 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
568 if v['version'] == 2}
579 if v['version'] == 2}
569
580
570 if permission not in ('push', 'pull'):
581 if permission not in ('push', 'pull'):
571 raise error.ProgrammingError('invalid wire protocol permission; '
582 raise error.ProgrammingError('invalid wire protocol permission; '
572 'got %s; expected "push" or "pull"' %
583 'got %s; expected "push" or "pull"' %
573 permission)
584 permission)
574
585
575 if args is None:
586 if args is None:
576 args = {}
587 args = {}
577
588
578 if not isinstance(args, dict):
589 if not isinstance(args, dict):
579 raise error.ProgrammingError('arguments for version 2 commands '
590 raise error.ProgrammingError('arguments for version 2 commands '
580 'must be declared as dicts')
591 'must be declared as dicts')
581
592
582 for arg, meta in args.items():
593 for arg, meta in args.items():
583 if arg == '*':
594 if arg == '*':
584 raise error.ProgrammingError('* argument name not allowed on '
595 raise error.ProgrammingError('* argument name not allowed on '
585 'version 2 commands')
596 'version 2 commands')
586
597
587 if not isinstance(meta, dict):
598 if not isinstance(meta, dict):
588 raise error.ProgrammingError('arguments for version 2 commands '
599 raise error.ProgrammingError('arguments for version 2 commands '
589 'must declare metadata as a dict')
600 'must declare metadata as a dict')
590
601
591 if 'type' not in meta:
602 if 'type' not in meta:
592 raise error.ProgrammingError('%s argument for command %s does not '
603 raise error.ProgrammingError('%s argument for command %s does not '
593 'declare type field' % (arg, name))
604 'declare type field' % (arg, name))
594
605
595 if meta['type'] not in ('bytes', 'int', 'list', 'dict', 'set', 'bool'):
606 if meta['type'] not in ('bytes', 'int', 'list', 'dict', 'set', 'bool'):
596 raise error.ProgrammingError('%s argument for command %s has '
607 raise error.ProgrammingError('%s argument for command %s has '
597 'illegal type: %s' % (arg, name,
608 'illegal type: %s' % (arg, name,
598 meta['type']))
609 meta['type']))
599
610
600 if 'example' not in meta:
611 if 'example' not in meta:
601 raise error.ProgrammingError('%s argument for command %s does not '
612 raise error.ProgrammingError('%s argument for command %s does not '
602 'declare example field' % (arg, name))
613 'declare example field' % (arg, name))
603
614
604 if 'default' in meta and meta.get('required'):
615 if 'default' in meta and meta.get('required'):
605 raise error.ProgrammingError('%s argument for command %s is marked '
616 raise error.ProgrammingError('%s argument for command %s is marked '
606 'as required but has a default value' %
617 'as required but has a default value' %
607 (arg, name))
618 (arg, name))
608
619
609 meta.setdefault('default', lambda: None)
620 meta.setdefault('default', lambda: None)
610 meta.setdefault('required', False)
621 meta.setdefault('required', False)
611
622
612 def register(func):
623 def register(func):
613 if name in COMMANDS:
624 if name in COMMANDS:
614 raise error.ProgrammingError('%s command already registered '
625 raise error.ProgrammingError('%s command already registered '
615 'for version 2' % name)
626 'for version 2' % name)
616
627
617 COMMANDS[name] = wireprototypes.commandentry(
628 COMMANDS[name] = wireprototypes.commandentry(
618 func, args=args, transports=transports, permission=permission)
629 func, args=args, transports=transports, permission=permission)
619
630
620 return func
631 return func
621
632
622 return register
633 return register
623
634
624 @wireprotocommand('branchmap', permission='pull')
635 @wireprotocommand('branchmap', permission='pull')
625 def branchmapv2(repo, proto):
636 def branchmapv2(repo, proto):
626 yield {encoding.fromlocal(k): v
637 yield {encoding.fromlocal(k): v
627 for k, v in repo.branchmap().iteritems()}
638 for k, v in repo.branchmap().iteritems()}
628
639
629 @wireprotocommand('capabilities', permission='pull')
640 @wireprotocommand('capabilities', permission='pull')
630 def capabilitiesv2(repo, proto):
641 def capabilitiesv2(repo, proto):
631 yield _capabilitiesv2(repo, proto)
642 yield _capabilitiesv2(repo, proto)
632
643
633 @wireprotocommand(
644 @wireprotocommand(
634 'changesetdata',
645 'changesetdata',
635 args={
646 args={
636 'noderange': {
647 'noderange': {
637 'type': 'list',
648 'type': 'list',
638 'example': [[b'0123456...'], [b'abcdef...']],
649 'example': [[b'0123456...'], [b'abcdef...']],
639 },
650 },
640 'nodes': {
651 'nodes': {
641 'type': 'list',
652 'type': 'list',
642 'example': [b'0123456...'],
653 'example': [b'0123456...'],
643 },
654 },
644 'fields': {
655 'fields': {
645 'type': 'set',
656 'type': 'set',
646 'default': set,
657 'default': set,
647 'example': {b'parents', b'revision'},
658 'example': {b'parents', b'revision'},
648 },
659 },
649 },
660 },
650 permission='pull')
661 permission='pull')
651 def changesetdata(repo, proto, noderange, nodes, fields):
662 def changesetdata(repo, proto, noderange, nodes, fields):
652 # TODO look for unknown fields and abort when they can't be serviced.
663 # TODO look for unknown fields and abort when they can't be serviced.
653
664
654 if noderange is None and nodes is None:
665 if noderange is None and nodes is None:
655 raise error.WireprotoCommandError(
666 raise error.WireprotoCommandError(
656 'noderange or nodes must be defined')
667 'noderange or nodes must be defined')
657
668
658 if noderange is not None:
669 if noderange is not None:
659 if len(noderange) != 2:
670 if len(noderange) != 2:
660 raise error.WireprotoCommandError(
671 raise error.WireprotoCommandError(
661 'noderange must consist of 2 elements')
672 'noderange must consist of 2 elements')
662
673
663 if not noderange[1]:
674 if not noderange[1]:
664 raise error.WireprotoCommandError(
675 raise error.WireprotoCommandError(
665 'heads in noderange request cannot be empty')
676 'heads in noderange request cannot be empty')
666
677
667 cl = repo.changelog
678 cl = repo.changelog
668 hasnode = cl.hasnode
679 hasnode = cl.hasnode
669
680
670 seen = set()
681 seen = set()
671 outgoing = []
682 outgoing = []
672
683
673 if nodes is not None:
684 if nodes is not None:
674 outgoing.extend(n for n in nodes if hasnode(n))
685 outgoing.extend(n for n in nodes if hasnode(n))
675 seen |= set(outgoing)
686 seen |= set(outgoing)
676
687
677 if noderange is not None:
688 if noderange is not None:
678 if noderange[0]:
689 if noderange[0]:
679 common = [n for n in noderange[0] if hasnode(n)]
690 common = [n for n in noderange[0] if hasnode(n)]
680 else:
691 else:
681 common = [nullid]
692 common = [nullid]
682
693
683 for n in discovery.outgoing(repo, common, noderange[1]).missing:
694 for n in discovery.outgoing(repo, common, noderange[1]).missing:
684 if n not in seen:
695 if n not in seen:
685 outgoing.append(n)
696 outgoing.append(n)
686 # Don't need to add to seen here because this is the final
697 # Don't need to add to seen here because this is the final
687 # source of nodes and there should be no duplicates in this
698 # source of nodes and there should be no duplicates in this
688 # list.
699 # list.
689
700
690 seen.clear()
701 seen.clear()
691 publishing = repo.publishing()
702 publishing = repo.publishing()
692
703
693 if outgoing:
704 if outgoing:
694 repo.hook('preoutgoing', throw=True, source='serve')
705 repo.hook('preoutgoing', throw=True, source='serve')
695
706
696 yield {
707 yield {
697 b'totalitems': len(outgoing),
708 b'totalitems': len(outgoing),
698 }
709 }
699
710
700 # The phases of nodes already transferred to the client may have changed
711 # The phases of nodes already transferred to the client may have changed
701 # since the client last requested data. We send phase-only records
712 # since the client last requested data. We send phase-only records
702 # for these revisions, if requested.
713 # for these revisions, if requested.
703 if b'phase' in fields and noderange is not None:
714 if b'phase' in fields and noderange is not None:
704 # TODO skip nodes whose phase will be reflected by a node in the
715 # TODO skip nodes whose phase will be reflected by a node in the
705 # outgoing set. This is purely an optimization to reduce data
716 # outgoing set. This is purely an optimization to reduce data
706 # size.
717 # size.
707 for node in noderange[0]:
718 for node in noderange[0]:
708 yield {
719 yield {
709 b'node': node,
720 b'node': node,
710 b'phase': b'public' if publishing else repo[node].phasestr()
721 b'phase': b'public' if publishing else repo[node].phasestr()
711 }
722 }
712
723
713 nodebookmarks = {}
724 nodebookmarks = {}
714 for mark, node in repo._bookmarks.items():
725 for mark, node in repo._bookmarks.items():
715 nodebookmarks.setdefault(node, set()).add(mark)
726 nodebookmarks.setdefault(node, set()).add(mark)
716
727
717 # It is already topologically sorted by revision number.
728 # It is already topologically sorted by revision number.
718 for node in outgoing:
729 for node in outgoing:
719 d = {
730 d = {
720 b'node': node,
731 b'node': node,
721 }
732 }
722
733
723 if b'parents' in fields:
734 if b'parents' in fields:
724 d[b'parents'] = cl.parents(node)
735 d[b'parents'] = cl.parents(node)
725
736
726 if b'phase' in fields:
737 if b'phase' in fields:
727 if publishing:
738 if publishing:
728 d[b'phase'] = b'public'
739 d[b'phase'] = b'public'
729 else:
740 else:
730 ctx = repo[node]
741 ctx = repo[node]
731 d[b'phase'] = ctx.phasestr()
742 d[b'phase'] = ctx.phasestr()
732
743
733 if b'bookmarks' in fields and node in nodebookmarks:
744 if b'bookmarks' in fields and node in nodebookmarks:
734 d[b'bookmarks'] = sorted(nodebookmarks[node])
745 d[b'bookmarks'] = sorted(nodebookmarks[node])
735 del nodebookmarks[node]
746 del nodebookmarks[node]
736
747
737 revisiondata = None
748 revisiondata = None
738
749
739 if b'revision' in fields:
750 if b'revision' in fields:
740 revisiondata = cl.revision(node, raw=True)
751 revisiondata = cl.revision(node, raw=True)
741 d[b'revisionsize'] = len(revisiondata)
752 d[b'revisionsize'] = len(revisiondata)
742
753
743 # TODO make it possible for extensions to wrap a function or register
754 # TODO make it possible for extensions to wrap a function or register
744 # a handler to service custom fields.
755 # a handler to service custom fields.
745
756
746 yield d
757 yield d
747
758
748 if revisiondata is not None:
759 if revisiondata is not None:
749 yield revisiondata
760 yield revisiondata
750
761
751 # If requested, send bookmarks from nodes that didn't have revision
762 # If requested, send bookmarks from nodes that didn't have revision
752 # data sent so receiver is aware of any bookmark updates.
763 # data sent so receiver is aware of any bookmark updates.
753 if b'bookmarks' in fields:
764 if b'bookmarks' in fields:
754 for node, marks in sorted(nodebookmarks.iteritems()):
765 for node, marks in sorted(nodebookmarks.iteritems()):
755 yield {
766 yield {
756 b'node': node,
767 b'node': node,
757 b'bookmarks': sorted(marks),
768 b'bookmarks': sorted(marks),
758 }
769 }
759
770
760 class FileAccessError(Exception):
771 class FileAccessError(Exception):
761 """Represents an error accessing a specific file."""
772 """Represents an error accessing a specific file."""
762
773
763 def __init__(self, path, msg, args):
774 def __init__(self, path, msg, args):
764 self.path = path
775 self.path = path
765 self.msg = msg
776 self.msg = msg
766 self.args = args
777 self.args = args
767
778
768 def getfilestore(repo, proto, path):
779 def getfilestore(repo, proto, path):
769 """Obtain a file storage object for use with wire protocol.
780 """Obtain a file storage object for use with wire protocol.
770
781
771 Exists as a standalone function so extensions can monkeypatch to add
782 Exists as a standalone function so extensions can monkeypatch to add
772 access control.
783 access control.
773 """
784 """
774 # This seems to work even if the file doesn't exist. So catch
785 # This seems to work even if the file doesn't exist. So catch
775 # "empty" files and return an error.
786 # "empty" files and return an error.
776 fl = repo.file(path)
787 fl = repo.file(path)
777
788
778 if not len(fl):
789 if not len(fl):
779 raise FileAccessError(path, 'unknown file: %s', (path,))
790 raise FileAccessError(path, 'unknown file: %s', (path,))
780
791
781 return fl
792 return fl
782
793
783 @wireprotocommand(
794 @wireprotocommand(
784 'filedata',
795 'filedata',
785 args={
796 args={
786 'haveparents': {
797 'haveparents': {
787 'type': 'bool',
798 'type': 'bool',
788 'default': lambda: False,
799 'default': lambda: False,
789 'example': True,
800 'example': True,
790 },
801 },
791 'nodes': {
802 'nodes': {
792 'type': 'list',
803 'type': 'list',
793 'required': True,
804 'required': True,
794 'example': [b'0123456...'],
805 'example': [b'0123456...'],
795 },
806 },
796 'fields': {
807 'fields': {
797 'type': 'set',
808 'type': 'set',
798 'default': set,
809 'default': set,
799 'example': {b'parents', b'revision'},
810 'example': {b'parents', b'revision'},
800 },
811 },
801 'path': {
812 'path': {
802 'type': 'bytes',
813 'type': 'bytes',
803 'required': True,
814 'required': True,
804 'example': b'foo.txt',
815 'example': b'foo.txt',
805 }
816 }
806 },
817 },
807 permission='pull')
818 permission='pull')
808 def filedata(repo, proto, haveparents, nodes, fields, path):
819 def filedata(repo, proto, haveparents, nodes, fields, path):
809 try:
820 try:
810 # Extensions may wish to access the protocol handler.
821 # Extensions may wish to access the protocol handler.
811 store = getfilestore(repo, proto, path)
822 store = getfilestore(repo, proto, path)
812 except FileAccessError as e:
823 except FileAccessError as e:
813 raise error.WireprotoCommandError(e.msg, e.args)
824 raise error.WireprotoCommandError(e.msg, e.args)
814
825
815 # Validate requested nodes.
826 # Validate requested nodes.
816 for node in nodes:
827 for node in nodes:
817 try:
828 try:
818 store.rev(node)
829 store.rev(node)
819 except error.LookupError:
830 except error.LookupError:
820 raise error.WireprotoCommandError('unknown file node: %s',
831 raise error.WireprotoCommandError('unknown file node: %s',
821 (hex(node),))
832 (hex(node),))
822
833
823 revs, requests = builddeltarequests(store, nodes, haveparents)
834 revs, requests = builddeltarequests(store, nodes, haveparents)
824
835
825 yield {
836 yield {
826 b'totalitems': len(revs),
837 b'totalitems': len(revs),
827 }
838 }
828
839
829 if b'revision' in fields:
840 if b'revision' in fields:
830 deltas = store.emitrevisiondeltas(requests)
841 deltas = store.emitrevisiondeltas(requests)
831 else:
842 else:
832 deltas = None
843 deltas = None
833
844
834 for rev in revs:
845 for rev in revs:
835 node = store.node(rev)
846 node = store.node(rev)
836
847
837 if deltas is not None:
848 if deltas is not None:
838 delta = next(deltas)
849 delta = next(deltas)
839 else:
850 else:
840 delta = None
851 delta = None
841
852
842 d = {
853 d = {
843 b'node': node,
854 b'node': node,
844 }
855 }
845
856
846 if b'parents' in fields:
857 if b'parents' in fields:
847 d[b'parents'] = store.parents(node)
858 d[b'parents'] = store.parents(node)
848
859
849 if b'revision' in fields:
860 if b'revision' in fields:
850 assert delta is not None
861 assert delta is not None
851 assert delta.flags == 0
862 assert delta.flags == 0
852 assert d[b'node'] == delta.node
863 assert d[b'node'] == delta.node
853
864
854 if delta.revision is not None:
865 if delta.revision is not None:
855 revisiondata = delta.revision
866 revisiondata = delta.revision
856 d[b'revisionsize'] = len(revisiondata)
867 d[b'revisionsize'] = len(revisiondata)
857 else:
868 else:
858 d[b'deltabasenode'] = delta.basenode
869 d[b'deltabasenode'] = delta.basenode
859 revisiondata = delta.delta
870 revisiondata = delta.delta
860 d[b'deltasize'] = len(revisiondata)
871 d[b'deltasize'] = len(revisiondata)
861 else:
872 else:
862 revisiondata = None
873 revisiondata = None
863
874
864 yield d
875 yield d
865
876
866 if revisiondata is not None:
877 if revisiondata is not None:
867 yield revisiondata
878 yield revisiondata
868
879
869 if deltas is not None:
880 if deltas is not None:
870 try:
881 try:
871 next(deltas)
882 next(deltas)
872 raise error.ProgrammingError('should not have more deltas')
883 raise error.ProgrammingError('should not have more deltas')
873 except GeneratorExit:
884 except GeneratorExit:
874 pass
885 pass
875
886
876 @wireprotocommand(
887 @wireprotocommand(
877 'heads',
888 'heads',
878 args={
889 args={
879 'publiconly': {
890 'publiconly': {
880 'type': 'bool',
891 'type': 'bool',
881 'default': lambda: False,
892 'default': lambda: False,
882 'example': False,
893 'example': False,
883 },
894 },
884 },
895 },
885 permission='pull')
896 permission='pull')
886 def headsv2(repo, proto, publiconly):
897 def headsv2(repo, proto, publiconly):
887 if publiconly:
898 if publiconly:
888 repo = repo.filtered('immutable')
899 repo = repo.filtered('immutable')
889
900
890 yield repo.heads()
901 yield repo.heads()
891
902
892 @wireprotocommand(
903 @wireprotocommand(
893 'known',
904 'known',
894 args={
905 args={
895 'nodes': {
906 'nodes': {
896 'type': 'list',
907 'type': 'list',
897 'default': list,
908 'default': list,
898 'example': [b'deadbeef'],
909 'example': [b'deadbeef'],
899 },
910 },
900 },
911 },
901 permission='pull')
912 permission='pull')
902 def knownv2(repo, proto, nodes):
913 def knownv2(repo, proto, nodes):
903 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
914 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
904 yield result
915 yield result
905
916
906 @wireprotocommand(
917 @wireprotocommand(
907 'listkeys',
918 'listkeys',
908 args={
919 args={
909 'namespace': {
920 'namespace': {
910 'type': 'bytes',
921 'type': 'bytes',
911 'required': True,
922 'required': True,
912 'example': b'ns',
923 'example': b'ns',
913 },
924 },
914 },
925 },
915 permission='pull')
926 permission='pull')
916 def listkeysv2(repo, proto, namespace):
927 def listkeysv2(repo, proto, namespace):
917 keys = repo.listkeys(encoding.tolocal(namespace))
928 keys = repo.listkeys(encoding.tolocal(namespace))
918 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
929 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
919 for k, v in keys.iteritems()}
930 for k, v in keys.iteritems()}
920
931
921 yield keys
932 yield keys
922
933
923 @wireprotocommand(
934 @wireprotocommand(
924 'lookup',
935 'lookup',
925 args={
936 args={
926 'key': {
937 'key': {
927 'type': 'bytes',
938 'type': 'bytes',
928 'required': True,
939 'required': True,
929 'example': b'foo',
940 'example': b'foo',
930 },
941 },
931 },
942 },
932 permission='pull')
943 permission='pull')
933 def lookupv2(repo, proto, key):
944 def lookupv2(repo, proto, key):
934 key = encoding.tolocal(key)
945 key = encoding.tolocal(key)
935
946
936 # TODO handle exception.
947 # TODO handle exception.
937 node = repo.lookup(key)
948 node = repo.lookup(key)
938
949
939 yield node
950 yield node
940
951
941 @wireprotocommand(
952 @wireprotocommand(
942 'manifestdata',
953 'manifestdata',
943 args={
954 args={
944 'nodes': {
955 'nodes': {
945 'type': 'list',
956 'type': 'list',
946 'required': True,
957 'required': True,
947 'example': [b'0123456...'],
958 'example': [b'0123456...'],
948 },
959 },
949 'haveparents': {
960 'haveparents': {
950 'type': 'bool',
961 'type': 'bool',
951 'default': lambda: False,
962 'default': lambda: False,
952 'example': True,
963 'example': True,
953 },
964 },
954 'fields': {
965 'fields': {
955 'type': 'set',
966 'type': 'set',
956 'default': set,
967 'default': set,
957 'example': {b'parents', b'revision'},
968 'example': {b'parents', b'revision'},
958 },
969 },
959 'tree': {
970 'tree': {
960 'type': 'bytes',
971 'type': 'bytes',
961 'required': True,
972 'required': True,
962 'example': b'',
973 'example': b'',
963 },
974 },
964 },
975 },
965 permission='pull')
976 permission='pull')
966 def manifestdata(repo, proto, haveparents, nodes, fields, tree):
977 def manifestdata(repo, proto, haveparents, nodes, fields, tree):
967 store = repo.manifestlog.getstorage(tree)
978 store = repo.manifestlog.getstorage(tree)
968
979
969 # Validate the node is known and abort on unknown revisions.
980 # Validate the node is known and abort on unknown revisions.
970 for node in nodes:
981 for node in nodes:
971 try:
982 try:
972 store.rev(node)
983 store.rev(node)
973 except error.LookupError:
984 except error.LookupError:
974 raise error.WireprotoCommandError(
985 raise error.WireprotoCommandError(
975 'unknown node: %s', (node,))
986 'unknown node: %s', (node,))
976
987
977 revs, requests = builddeltarequests(store, nodes, haveparents)
988 revs, requests = builddeltarequests(store, nodes, haveparents)
978
989
979 yield {
990 yield {
980 b'totalitems': len(revs),
991 b'totalitems': len(revs),
981 }
992 }
982
993
983 if b'revision' in fields:
994 if b'revision' in fields:
984 deltas = store.emitrevisiondeltas(requests)
995 deltas = store.emitrevisiondeltas(requests)
985 else:
996 else:
986 deltas = None
997 deltas = None
987
998
988 for rev in revs:
999 for rev in revs:
989 node = store.node(rev)
1000 node = store.node(rev)
990
1001
991 if deltas is not None:
1002 if deltas is not None:
992 delta = next(deltas)
1003 delta = next(deltas)
993 else:
1004 else:
994 delta = None
1005 delta = None
995
1006
996 d = {
1007 d = {
997 b'node': node,
1008 b'node': node,
998 }
1009 }
999
1010
1000 if b'parents' in fields:
1011 if b'parents' in fields:
1001 d[b'parents'] = store.parents(node)
1012 d[b'parents'] = store.parents(node)
1002
1013
1003 if b'revision' in fields:
1014 if b'revision' in fields:
1004 assert delta is not None
1015 assert delta is not None
1005 assert delta.flags == 0
1016 assert delta.flags == 0
1006 assert d[b'node'] == delta.node
1017 assert d[b'node'] == delta.node
1007
1018
1008 if delta.revision is not None:
1019 if delta.revision is not None:
1009 revisiondata = delta.revision
1020 revisiondata = delta.revision
1010 d[b'revisionsize'] = len(revisiondata)
1021 d[b'revisionsize'] = len(revisiondata)
1011 else:
1022 else:
1012 d[b'deltabasenode'] = delta.basenode
1023 d[b'deltabasenode'] = delta.basenode
1013 revisiondata = delta.delta
1024 revisiondata = delta.delta
1014 d[b'deltasize'] = len(revisiondata)
1025 d[b'deltasize'] = len(revisiondata)
1015 else:
1026 else:
1016 revisiondata = None
1027 revisiondata = None
1017
1028
1018 yield d
1029 yield d
1019
1030
1020 if revisiondata is not None:
1031 if revisiondata is not None:
1021 yield revisiondata
1032 yield revisiondata
1022
1033
1023 if deltas is not None:
1034 if deltas is not None:
1024 try:
1035 try:
1025 next(deltas)
1036 next(deltas)
1026 raise error.ProgrammingError('should not have more deltas')
1037 raise error.ProgrammingError('should not have more deltas')
1027 except GeneratorExit:
1038 except GeneratorExit:
1028 pass
1039 pass
1029
1040
1030 @wireprotocommand(
1041 @wireprotocommand(
1031 'pushkey',
1042 'pushkey',
1032 args={
1043 args={
1033 'namespace': {
1044 'namespace': {
1034 'type': 'bytes',
1045 'type': 'bytes',
1035 'required': True,
1046 'required': True,
1036 'example': b'ns',
1047 'example': b'ns',
1037 },
1048 },
1038 'key': {
1049 'key': {
1039 'type': 'bytes',
1050 'type': 'bytes',
1040 'required': True,
1051 'required': True,
1041 'example': b'key',
1052 'example': b'key',
1042 },
1053 },
1043 'old': {
1054 'old': {
1044 'type': 'bytes',
1055 'type': 'bytes',
1045 'required': True,
1056 'required': True,
1046 'example': b'old',
1057 'example': b'old',
1047 },
1058 },
1048 'new': {
1059 'new': {
1049 'type': 'bytes',
1060 'type': 'bytes',
1050 'required': True,
1061 'required': True,
1051 'example': 'new',
1062 'example': 'new',
1052 },
1063 },
1053 },
1064 },
1054 permission='push')
1065 permission='push')
1055 def pushkeyv2(repo, proto, namespace, key, old, new):
1066 def pushkeyv2(repo, proto, namespace, key, old, new):
1056 # TODO handle ui output redirection
1067 # TODO handle ui output redirection
1057 yield repo.pushkey(encoding.tolocal(namespace),
1068 yield repo.pushkey(encoding.tolocal(namespace),
1058 encoding.tolocal(key),
1069 encoding.tolocal(key),
1059 encoding.tolocal(old),
1070 encoding.tolocal(old),
1060 encoding.tolocal(new))
1071 encoding.tolocal(new))
@@ -1,749 +1,749 b''
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$ 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$ 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$ 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$ 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\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...DpathGfoo.txtKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...Dtree@Kpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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: 467\r\n
427 s> Content-Length: 467\r\n
428 s> \r\n
428 s> \r\n
429 s> batch branchmap $USUAL_BUNDLE2_CAPS$ 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$ 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: 467\r\n
462 s> Content-Length: 467\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$ 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$ 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: 467\r\n
726 s> Content-Length: 467\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$ 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$ 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,544 +1,628 b''
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$ 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$ 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$ 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$ 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$ 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$ 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$ 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$ 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\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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$ 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$ 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\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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$ 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$ 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\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...DpathGfoo.txtKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...Dtree@Kpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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': {
236 b'parents',
236 b'default': set([]),
237 b'revision'
237 b'required': False,
238 ]),
238 b'type': b'set'
239 b'noderange': [
239 },
240 [
240 b'noderange': {
241 b'0123456...'
241 b'default': None,
242 ],
242 b'required': False,
243 [
243 b'type': b'list'
244 b'abcdef...'
244 },
245 ]
245 b'nodes': {
246 ],
246 b'default': None,
247 b'nodes': [
247 b'required': False,
248 b'0123456...'
248 b'type': b'list'
249 ]
249 }
250 },
250 },
251 b'permissions': [
251 b'permissions': [
252 b'pull'
252 b'pull'
253 ]
253 ]
254 },
254 },
255 b'filedata': {
255 b'filedata': {
256 b'args': {
256 b'args': {
257 b'fields': set([
257 b'fields': {
258 b'parents',
258 b'default': set([]),
259 b'revision'
259 b'required': False,
260 ]),
260 b'type': b'set'
261 b'haveparents': True,
261 },
262 b'nodes': [
262 b'haveparents': {
263 b'0123456...'
263 b'default': False,
264 ],
264 b'required': False,
265 b'path': b'foo.txt'
265 b'type': b'bool'
266 },
267 b'nodes': {
268 b'required': True,
269 b'type': b'list'
270 },
271 b'path': {
272 b'required': True,
273 b'type': b'bytes'
274 }
266 },
275 },
267 b'permissions': [
276 b'permissions': [
268 b'pull'
277 b'pull'
269 ]
278 ]
270 },
279 },
271 b'heads': {
280 b'heads': {
272 b'args': {
281 b'args': {
273 b'publiconly': False
282 b'publiconly': {
283 b'default': False,
284 b'required': False,
285 b'type': b'bool'
286 }
274 },
287 },
275 b'permissions': [
288 b'permissions': [
276 b'pull'
289 b'pull'
277 ]
290 ]
278 },
291 },
279 b'known': {
292 b'known': {
280 b'args': {
293 b'args': {
281 b'nodes': [
294 b'nodes': {
282 b'deadbeef'
295 b'default': [],
283 ]
296 b'required': False,
297 b'type': b'list'
298 }
284 },
299 },
285 b'permissions': [
300 b'permissions': [
286 b'pull'
301 b'pull'
287 ]
302 ]
288 },
303 },
289 b'listkeys': {
304 b'listkeys': {
290 b'args': {
305 b'args': {
291 b'namespace': b'ns'
306 b'namespace': {
307 b'required': True,
308 b'type': b'bytes'
309 }
292 },
310 },
293 b'permissions': [
311 b'permissions': [
294 b'pull'
312 b'pull'
295 ]
313 ]
296 },
314 },
297 b'lookup': {
315 b'lookup': {
298 b'args': {
316 b'args': {
299 b'key': b'foo'
317 b'key': {
318 b'required': True,
319 b'type': b'bytes'
320 }
300 },
321 },
301 b'permissions': [
322 b'permissions': [
302 b'pull'
323 b'pull'
303 ]
324 ]
304 },
325 },
305 b'manifestdata': {
326 b'manifestdata': {
306 b'args': {
327 b'args': {
307 b'fields': set([
328 b'fields': {
308 b'parents',
329 b'default': set([]),
309 b'revision'
330 b'required': False,
310 ]),
331 b'type': b'set'
311 b'haveparents': True,
332 },
312 b'nodes': [
333 b'haveparents': {
313 b'0123456...'
334 b'default': False,
314 ],
335 b'required': False,
315 b'tree': b''
336 b'type': b'bool'
337 },
338 b'nodes': {
339 b'required': True,
340 b'type': b'list'
341 },
342 b'tree': {
343 b'required': True,
344 b'type': b'bytes'
345 }
316 },
346 },
317 b'permissions': [
347 b'permissions': [
318 b'pull'
348 b'pull'
319 ]
349 ]
320 },
350 },
321 b'pushkey': {
351 b'pushkey': {
322 b'args': {
352 b'args': {
323 b'key': b'key',
353 b'key': {
324 b'namespace': b'ns',
354 b'required': True,
325 b'new': b'new',
355 b'type': b'bytes'
326 b'old': b'old'
356 },
357 b'namespace': {
358 b'required': True,
359 b'type': b'bytes'
360 },
361 b'new': {
362 b'required': True,
363 b'type': b'bytes'
364 },
365 b'old': {
366 b'required': True,
367 b'type': b'bytes'
368 }
327 },
369 },
328 b'permissions': [
370 b'permissions': [
329 b'push'
371 b'push'
330 ]
372 ]
331 }
373 }
332 },
374 },
333 b'compression': [
375 b'compression': [
334 {
376 {
335 b'name': b'zlib'
377 b'name': b'zlib'
336 }
378 }
337 ],
379 ],
338 b'framingmediatypes': [
380 b'framingmediatypes': [
339 b'application/mercurial-exp-framing-0005'
381 b'application/mercurial-exp-framing-0005'
340 ],
382 ],
341 b'pathfilterprefixes': set([
383 b'pathfilterprefixes': set([
342 b'path:',
384 b'path:',
343 b'rootfilesin:'
385 b'rootfilesin:'
344 ]),
386 ]),
345 b'rawrepoformats': [
387 b'rawrepoformats': [
346 b'generaldelta',
388 b'generaldelta',
347 b'revlogv1'
389 b'revlogv1'
348 ]
390 ]
349 }
391 }
350 },
392 },
351 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ 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'
393 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ 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'
352 }
394 }
353
395
354 capabilities command returns expected info
396 capabilities command returns expected info
355
397
356 $ sendhttpv2peerhandshake << EOF
398 $ sendhttpv2peerhandshake << EOF
357 > command capabilities
399 > command capabilities
358 > EOF
400 > EOF
359 creating http peer for wire protocol version 2
401 creating http peer for wire protocol version 2
360 s> GET /?cmd=capabilities HTTP/1.1\r\n
402 s> GET /?cmd=capabilities HTTP/1.1\r\n
361 s> Accept-Encoding: identity\r\n
403 s> Accept-Encoding: identity\r\n
362 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
404 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
363 s> x-hgproto-1: cbor\r\n
405 s> x-hgproto-1: cbor\r\n
364 s> x-hgupgrade-1: exp-http-v2-0001\r\n
406 s> x-hgupgrade-1: exp-http-v2-0001\r\n
365 s> accept: application/mercurial-0.1\r\n
407 s> accept: application/mercurial-0.1\r\n
366 s> host: $LOCALIP:$HGPORT\r\n (glob)
408 s> host: $LOCALIP:$HGPORT\r\n (glob)
367 s> user-agent: Mercurial debugwireproto\r\n
409 s> user-agent: Mercurial debugwireproto\r\n
368 s> \r\n
410 s> \r\n
369 s> makefile('rb', None)
411 s> makefile('rb', None)
370 s> HTTP/1.1 200 OK\r\n
412 s> HTTP/1.1 200 OK\r\n
371 s> Server: testing stub value\r\n
413 s> Server: testing stub value\r\n
372 s> Date: $HTTP_DATE$\r\n
414 s> Date: $HTTP_DATE$\r\n
373 s> Content-Type: application/mercurial-cbor\r\n
415 s> Content-Type: application/mercurial-cbor\r\n
374 s> Content-Length: *\r\n (glob)
416 s> Content-Length: *\r\n (glob)
375 s> \r\n
417 s> \r\n
376 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...DpathGfoo.txtKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...Dtree@Kpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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
418 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ 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
377 sending capabilities command
419 sending capabilities command
378 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
420 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
379 s> Accept-Encoding: identity\r\n
421 s> Accept-Encoding: identity\r\n
380 s> accept: application/mercurial-exp-framing-0005\r\n
422 s> accept: application/mercurial-exp-framing-0005\r\n
381 s> content-type: application/mercurial-exp-framing-0005\r\n
423 s> content-type: application/mercurial-exp-framing-0005\r\n
382 s> content-length: 27\r\n
424 s> content-length: 27\r\n
383 s> host: $LOCALIP:$HGPORT\r\n (glob)
425 s> host: $LOCALIP:$HGPORT\r\n (glob)
384 s> user-agent: Mercurial debugwireproto\r\n
426 s> user-agent: Mercurial debugwireproto\r\n
385 s> \r\n
427 s> \r\n
386 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
428 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
387 s> makefile('rb', None)
429 s> makefile('rb', None)
388 s> HTTP/1.1 200 OK\r\n
430 s> HTTP/1.1 200 OK\r\n
389 s> Server: testing stub value\r\n
431 s> Server: testing stub value\r\n
390 s> Date: $HTTP_DATE$\r\n
432 s> Date: $HTTP_DATE$\r\n
391 s> Content-Type: application/mercurial-exp-framing-0005\r\n
433 s> Content-Type: application/mercurial-exp-framing-0005\r\n
392 s> Transfer-Encoding: chunked\r\n
434 s> Transfer-Encoding: chunked\r\n
393 s> \r\n
435 s> \r\n
394 s> 13\r\n
436 s> 13\r\n
395 s> \x0b\x00\x00\x01\x00\x02\x011
437 s> \x0b\x00\x00\x01\x00\x02\x011
396 s> \xa1FstatusBok
438 s> \xa1FstatusBok
397 s> \r\n
439 s> \r\n
398 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
440 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
399 s> 33e\r\n
441 s> 485\r\n
400 s> 6\x03\x00\x01\x00\x02\x001
442 s> }\x04\x00\x01\x00\x02\x001
401 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xd9\x01\x02\x82GparentsHrevisionInoderange\x82\x81J0123456...\x81Iabcdef...Enodes\x81J0123456...Kpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...DpathGfoo.txtKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xf5Enodes\x81J0123456...Dtree@Kpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1
443 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa3Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa3Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1
402 s> \r\n
444 s> \r\n
403 received frame(size=822; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
445 received frame(size=1149; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
404 s> 8\r\n
446 s> 8\r\n
405 s> \x00\x00\x00\x01\x00\x02\x002
447 s> \x00\x00\x00\x01\x00\x02\x002
406 s> \r\n
448 s> \r\n
407 s> 0\r\n
449 s> 0\r\n
408 s> \r\n
450 s> \r\n
409 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
451 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
410 response: gen[
452 response: gen[
411 {
453 {
412 b'commands': {
454 b'commands': {
413 b'branchmap': {
455 b'branchmap': {
414 b'args': {},
456 b'args': {},
415 b'permissions': [
457 b'permissions': [
416 b'pull'
458 b'pull'
417 ]
459 ]
418 },
460 },
419 b'capabilities': {
461 b'capabilities': {
420 b'args': {},
462 b'args': {},
421 b'permissions': [
463 b'permissions': [
422 b'pull'
464 b'pull'
423 ]
465 ]
424 },
466 },
425 b'changesetdata': {
467 b'changesetdata': {
426 b'args': {
468 b'args': {
427 b'fields': set([
469 b'fields': {
428 b'parents',
470 b'default': set([]),
429 b'revision'
471 b'required': False,
430 ]),
472 b'type': b'set'
431 b'noderange': [
473 },
432 [
474 b'noderange': {
433 b'0123456...'
475 b'default': None,
434 ],
476 b'required': False,
435 [
477 b'type': b'list'
436 b'abcdef...'
478 },
437 ]
479 b'nodes': {
438 ],
480 b'default': None,
439 b'nodes': [
481 b'required': False,
440 b'0123456...'
482 b'type': b'list'
441 ]
483 }
442 },
484 },
443 b'permissions': [
485 b'permissions': [
444 b'pull'
486 b'pull'
445 ]
487 ]
446 },
488 },
447 b'filedata': {
489 b'filedata': {
448 b'args': {
490 b'args': {
449 b'fields': set([
491 b'fields': {
450 b'parents',
492 b'default': set([]),
451 b'revision'
493 b'required': False,
452 ]),
494 b'type': b'set'
453 b'haveparents': True,
495 },
454 b'nodes': [
496 b'haveparents': {
455 b'0123456...'
497 b'default': False,
456 ],
498 b'required': False,
457 b'path': b'foo.txt'
499 b'type': b'bool'
500 },
501 b'nodes': {
502 b'required': True,
503 b'type': b'list'
504 },
505 b'path': {
506 b'required': True,
507 b'type': b'bytes'
508 }
458 },
509 },
459 b'permissions': [
510 b'permissions': [
460 b'pull'
511 b'pull'
461 ]
512 ]
462 },
513 },
463 b'heads': {
514 b'heads': {
464 b'args': {
515 b'args': {
465 b'publiconly': False
516 b'publiconly': {
517 b'default': False,
518 b'required': False,
519 b'type': b'bool'
520 }
466 },
521 },
467 b'permissions': [
522 b'permissions': [
468 b'pull'
523 b'pull'
469 ]
524 ]
470 },
525 },
471 b'known': {
526 b'known': {
472 b'args': {
527 b'args': {
473 b'nodes': [
528 b'nodes': {
474 b'deadbeef'
529 b'default': [],
475 ]
530 b'required': False,
531 b'type': b'list'
532 }
476 },
533 },
477 b'permissions': [
534 b'permissions': [
478 b'pull'
535 b'pull'
479 ]
536 ]
480 },
537 },
481 b'listkeys': {
538 b'listkeys': {
482 b'args': {
539 b'args': {
483 b'namespace': b'ns'
540 b'namespace': {
541 b'required': True,
542 b'type': b'bytes'
543 }
484 },
544 },
485 b'permissions': [
545 b'permissions': [
486 b'pull'
546 b'pull'
487 ]
547 ]
488 },
548 },
489 b'lookup': {
549 b'lookup': {
490 b'args': {
550 b'args': {
491 b'key': b'foo'
551 b'key': {
552 b'required': True,
553 b'type': b'bytes'
554 }
492 },
555 },
493 b'permissions': [
556 b'permissions': [
494 b'pull'
557 b'pull'
495 ]
558 ]
496 },
559 },
497 b'manifestdata': {
560 b'manifestdata': {
498 b'args': {
561 b'args': {
499 b'fields': set([
562 b'fields': {
500 b'parents',
563 b'default': set([]),
501 b'revision'
564 b'required': False,
502 ]),
565 b'type': b'set'
503 b'haveparents': True,
566 },
504 b'nodes': [
567 b'haveparents': {
505 b'0123456...'
568 b'default': False,
506 ],
569 b'required': False,
507 b'tree': b''
570 b'type': b'bool'
571 },
572 b'nodes': {
573 b'required': True,
574 b'type': b'list'
575 },
576 b'tree': {
577 b'required': True,
578 b'type': b'bytes'
579 }
508 },
580 },
509 b'permissions': [
581 b'permissions': [
510 b'pull'
582 b'pull'
511 ]
583 ]
512 },
584 },
513 b'pushkey': {
585 b'pushkey': {
514 b'args': {
586 b'args': {
515 b'key': b'key',
587 b'key': {
516 b'namespace': b'ns',
588 b'required': True,
517 b'new': b'new',
589 b'type': b'bytes'
518 b'old': b'old'
590 },
591 b'namespace': {
592 b'required': True,
593 b'type': b'bytes'
594 },
595 b'new': {
596 b'required': True,
597 b'type': b'bytes'
598 },
599 b'old': {
600 b'required': True,
601 b'type': b'bytes'
602 }
519 },
603 },
520 b'permissions': [
604 b'permissions': [
521 b'push'
605 b'push'
522 ]
606 ]
523 }
607 }
524 },
608 },
525 b'compression': [
609 b'compression': [
526 {
610 {
527 b'name': b'zlib'
611 b'name': b'zlib'
528 }
612 }
529 ],
613 ],
530 b'framingmediatypes': [
614 b'framingmediatypes': [
531 b'application/mercurial-exp-framing-0005'
615 b'application/mercurial-exp-framing-0005'
532 ],
616 ],
533 b'pathfilterprefixes': set([
617 b'pathfilterprefixes': set([
534 b'path:',
618 b'path:',
535 b'rootfilesin:'
619 b'rootfilesin:'
536 ]),
620 ]),
537 b'rawrepoformats': [
621 b'rawrepoformats': [
538 b'generaldelta',
622 b'generaldelta',
539 b'revlogv1'
623 b'revlogv1'
540 ]
624 ]
541 }
625 }
542 ]
626 ]
543
627
544 $ cat error.log
628 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now