##// END OF EJS Templates
wireproto: define and expose types of wire command arguments...
Gregory Szorc -
r37553:69e46c18 default
parent child Browse files
Show More
@@ -1,1774 +1,1779 b''
1 1 The Mercurial wire protocol is a request-response based protocol
2 2 with multiple wire representations.
3 3
4 4 Each request is modeled as a command name, a dictionary of arguments, and
5 5 optional raw input. Command arguments and their types are intrinsic
6 6 properties of commands. So is the response type of the command. This means
7 7 clients can't always send arbitrary arguments to servers and servers can't
8 8 return multiple response types.
9 9
10 10 The protocol is synchronous and does not support multiplexing (concurrent
11 11 commands).
12 12
13 13 Handshake
14 14 =========
15 15
16 16 It is required or common for clients to perform a *handshake* when connecting
17 17 to a server. The handshake serves the following purposes:
18 18
19 19 * Negotiating protocol/transport level options
20 20 * Allows the client to learn about server capabilities to influence
21 21 future requests
22 22 * Ensures the underlying transport channel is in a *clean* state
23 23
24 24 An important goal of the handshake is to allow clients to use more modern
25 25 wire protocol features. By default, clients must assume they are talking
26 26 to an old version of Mercurial server (possibly even the very first
27 27 implementation). So, clients should not attempt to call or utilize modern
28 28 wire protocol features until they have confirmation that the server
29 29 supports them. The handshake implementation is designed to allow both
30 30 ends to utilize the latest set of features and capabilities with as
31 31 few round trips as possible.
32 32
33 33 The handshake mechanism varies by transport and protocol and is documented
34 34 in the sections below.
35 35
36 36 HTTP Protocol
37 37 =============
38 38
39 39 Handshake
40 40 ---------
41 41
42 42 The client sends a ``capabilities`` command request (``?cmd=capabilities``)
43 43 as soon as HTTP requests may be issued.
44 44
45 45 The server responds with a capabilities string, which the client parses to
46 46 learn about the server's abilities.
47 47
48 48 HTTP Version 1 Transport
49 49 ------------------------
50 50
51 51 Commands are issued as HTTP/1.0 or HTTP/1.1 requests. Commands are
52 52 sent to the base URL of the repository with the command name sent in
53 53 the ``cmd`` query string parameter. e.g.
54 54 ``https://example.com/repo?cmd=capabilities``. The HTTP method is ``GET``
55 55 or ``POST`` depending on the command and whether there is a request
56 56 body.
57 57
58 58 Command arguments can be sent multiple ways.
59 59
60 60 The simplest is part of the URL query string using ``x-www-form-urlencoded``
61 61 encoding (see Python's ``urllib.urlencode()``. However, many servers impose
62 62 length limitations on the URL. So this mechanism is typically only used if
63 63 the server doesn't support other mechanisms.
64 64
65 65 If the server supports the ``httpheader`` capability, command arguments can
66 66 be sent in HTTP request headers named ``X-HgArg-<N>`` where ``<N>`` is an
67 67 integer starting at 1. A ``x-www-form-urlencoded`` representation of the
68 68 arguments is obtained. This full string is then split into chunks and sent
69 69 in numbered ``X-HgArg-<N>`` headers. The maximum length of each HTTP header
70 70 is defined by the server in the ``httpheader`` capability value, which defaults
71 71 to ``1024``. The server reassembles the encoded arguments string by
72 72 concatenating the ``X-HgArg-<N>`` headers then URL decodes them into a
73 73 dictionary.
74 74
75 75 The list of ``X-HgArg-<N>`` headers should be added to the ``Vary`` request
76 76 header to instruct caches to take these headers into consideration when caching
77 77 requests.
78 78
79 79 If the server supports the ``httppostargs`` capability, the client
80 80 may send command arguments in the HTTP request body as part of an
81 81 HTTP POST request. The command arguments will be URL encoded just like
82 82 they would for sending them via HTTP headers. However, no splitting is
83 83 performed: the raw arguments are included in the HTTP request body.
84 84
85 85 The client sends a ``X-HgArgs-Post`` header with the string length of the
86 86 encoded arguments data. Additional data may be included in the HTTP
87 87 request body immediately following the argument data. The offset of the
88 88 non-argument data is defined by the ``X-HgArgs-Post`` header. The
89 89 ``X-HgArgs-Post`` header is not required if there is no argument data.
90 90
91 91 Additional command data can be sent as part of the HTTP request body. The
92 92 default ``Content-Type`` when sending data is ``application/mercurial-0.1``.
93 93 A ``Content-Length`` header is currently always sent.
94 94
95 95 Example HTTP requests::
96 96
97 97 GET /repo?cmd=capabilities
98 98 X-HgArg-1: foo=bar&baz=hello%20world
99 99
100 100 The request media type should be chosen based on server support. If the
101 101 ``httpmediatype`` server capability is present, the client should send
102 102 the newest mutually supported media type. If this capability is absent,
103 103 the client must assume the server only supports the
104 104 ``application/mercurial-0.1`` media type.
105 105
106 106 The ``Content-Type`` HTTP response header identifies the response as coming
107 107 from Mercurial and can also be used to signal an error has occurred.
108 108
109 109 The ``application/mercurial-*`` media types indicate a generic Mercurial
110 110 data type.
111 111
112 112 The ``application/mercurial-0.1`` media type is raw Mercurial data. It is the
113 113 predecessor of the format below.
114 114
115 115 The ``application/mercurial-0.2`` media type is compression framed Mercurial
116 116 data. The first byte of the payload indicates the length of the compression
117 117 format identifier that follows. Next are N bytes indicating the compression
118 118 format. e.g. ``zlib``. The remaining bytes are compressed according to that
119 119 compression format. The decompressed data behaves the same as with
120 120 ``application/mercurial-0.1``.
121 121
122 122 The ``application/hg-error`` media type indicates a generic error occurred.
123 123 The content of the HTTP response body typically holds text describing the
124 124 error.
125 125
126 126 The ``application/hg-changegroup`` media type indicates a changegroup response
127 127 type.
128 128
129 129 Clients also accept the ``text/plain`` media type. All other media
130 130 types should cause the client to error.
131 131
132 132 Behavior of media types is further described in the ``Content Negotiation``
133 133 section below.
134 134
135 135 Clients should issue a ``User-Agent`` request header that identifies the client.
136 136 The server should not use the ``User-Agent`` for feature detection.
137 137
138 138 A command returning a ``string`` response issues a
139 139 ``application/mercurial-0.*`` media type and the HTTP response body contains
140 140 the raw string value (after compression decoding, if used). A
141 141 ``Content-Length`` header is typically issued, but not required.
142 142
143 143 A command returning a ``stream`` response issues a
144 144 ``application/mercurial-0.*`` media type and the HTTP response is typically
145 145 using *chunked transfer* (``Transfer-Encoding: chunked``).
146 146
147 147 HTTP Version 2 Transport
148 148 ------------------------
149 149
150 150 **Experimental - feature under active development**
151 151
152 152 Version 2 of the HTTP protocol is exposed under the ``/api/*`` URL space.
153 153 It's final API name is not yet formalized.
154 154
155 155 Commands are triggered by sending HTTP POST requests against URLs of the
156 156 form ``<permission>/<command>``, where ``<permission>`` is ``ro`` or
157 157 ``rw``, meaning read-only and read-write, respectively and ``<command>``
158 158 is a named wire protocol command.
159 159
160 160 Non-POST request methods MUST be rejected by the server with an HTTP
161 161 405 response.
162 162
163 163 Commands that modify repository state in meaningful ways MUST NOT be
164 164 exposed under the ``ro`` URL prefix. All available commands MUST be
165 165 available under the ``rw`` URL prefix.
166 166
167 167 Server adminstrators MAY implement blanket HTTP authentication keyed
168 168 off the URL prefix. For example, a server may require authentication
169 169 for all ``rw/*`` URLs and let unauthenticated requests to ``ro/*``
170 170 URL proceed. A server MAY issue an HTTP 401, 403, or 407 response
171 171 in accordance with RFC 7235. Clients SHOULD recognize the HTTP Basic
172 172 (RFC 7617) and Digest (RFC 7616) authentication schemes. Clients SHOULD
173 173 make an attempt to recognize unknown schemes using the
174 174 ``WWW-Authenticate`` response header on a 401 response, as defined by
175 175 RFC 7235.
176 176
177 177 Read-only commands are accessible under ``rw/*`` URLs so clients can
178 178 signal the intent of the operation very early in the connection
179 179 lifecycle. For example, a ``push`` operation - which consists of
180 180 various read-only commands mixed with at least one read-write command -
181 181 can perform all commands against ``rw/*`` URLs so that any server-side
182 182 authentication requirements are discovered upon attempting the first
183 183 command - not potentially several commands into the exchange. This
184 184 allows clients to fail faster or prompt for credentials as soon as the
185 185 exchange takes place. This provides a better end-user experience.
186 186
187 187 Requests to unknown commands or URLS result in an HTTP 404.
188 188 TODO formally define response type, how error is communicated, etc.
189 189
190 190 HTTP request and response bodies use the *Unified Frame-Based Protocol*
191 191 (defined below) for media exchange. The entirety of the HTTP message
192 192 body is 0 or more frames as defined by this protocol.
193 193
194 194 Clients and servers MUST advertise the ``TBD`` media type via the
195 195 ``Content-Type`` request and response headers. In addition, clients MUST
196 196 advertise this media type value in their ``Accept`` request header in all
197 197 requests.
198 198 TODO finalize the media type. For now, it is defined in wireprotoserver.py.
199 199
200 200 Servers receiving requests without an ``Accept`` header SHOULD respond with
201 201 an HTTP 406.
202 202
203 203 Servers receiving requests with an invalid ``Content-Type`` header SHOULD
204 204 respond with an HTTP 415.
205 205
206 206 The command to run is specified in the POST payload as defined by the
207 207 *Unified Frame-Based Protocol*. This is redundant with data already
208 208 encoded in the URL. This is by design, so server operators can have
209 209 better understanding about server activity from looking merely at
210 210 HTTP access logs.
211 211
212 212 In most circumstances, the command specified in the URL MUST match
213 213 the command specified in the frame-based payload or the server will
214 214 respond with an error. The exception to this is the special
215 215 ``multirequest`` URL. (See below.) In addition, HTTP requests
216 216 are limited to one command invocation. The exception is the special
217 217 ``multirequest`` URL.
218 218
219 219 The ``multirequest`` command endpoints (``ro/multirequest`` and
220 220 ``rw/multirequest``) are special in that they allow the execution of
221 221 *any* command and allow the execution of multiple commands. If the
222 222 HTTP request issues multiple commands across multiple frames, all
223 223 issued commands will be processed by the server. Per the defined
224 224 behavior of the *Unified Frame-Based Protocol*, commands may be
225 225 issued interleaved and responses may come back in a different order
226 226 than they were issued. Clients MUST be able to deal with this.
227 227
228 228 SSH Protocol
229 229 ============
230 230
231 231 Handshake
232 232 ---------
233 233
234 234 For all clients, the handshake consists of the client sending 1 or more
235 235 commands to the server using version 1 of the transport. Servers respond
236 236 to commands they know how to respond to and send an empty response (``0\n``)
237 237 for unknown commands (per standard behavior of version 1 of the transport).
238 238 Clients then typically look for a response to the newest sent command to
239 239 determine which transport version to use and what the available features for
240 240 the connection and server are.
241 241
242 242 Preceding any response from client-issued commands, the server may print
243 243 non-protocol output. It is common for SSH servers to print banners, message
244 244 of the day announcements, etc when clients connect. It is assumed that any
245 245 such *banner* output will precede any Mercurial server output. So clients
246 246 must be prepared to handle server output on initial connect that isn't
247 247 in response to any client-issued command and doesn't conform to Mercurial's
248 248 wire protocol. This *banner* output should only be on stdout. However,
249 249 some servers may send output on stderr.
250 250
251 251 Pre 0.9.1 clients issue a ``between`` command with the ``pairs`` argument
252 252 having the value
253 253 ``0000000000000000000000000000000000000000-0000000000000000000000000000000000000000``.
254 254
255 255 The ``between`` command has been supported since the original Mercurial
256 256 SSH server. Requesting the empty range will return a ``\n`` string response,
257 257 which will be encoded as ``1\n\n`` (value length of ``1`` followed by a newline
258 258 followed by the value, which happens to be a newline).
259 259
260 260 For pre 0.9.1 clients and all servers, the exchange looks like::
261 261
262 262 c: between\n
263 263 c: pairs 81\n
264 264 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
265 265 s: 1\n
266 266 s: \n
267 267
268 268 0.9.1+ clients send a ``hello`` command (with no arguments) before the
269 269 ``between`` command. The response to this command allows clients to
270 270 discover server capabilities and settings.
271 271
272 272 An example exchange between 0.9.1+ clients and a ``hello`` aware server looks
273 273 like::
274 274
275 275 c: hello\n
276 276 c: between\n
277 277 c: pairs 81\n
278 278 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
279 279 s: 324\n
280 280 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
281 281 s: 1\n
282 282 s: \n
283 283
284 284 And a similar scenario but with servers sending a banner on connect::
285 285
286 286 c: hello\n
287 287 c: between\n
288 288 c: pairs 81\n
289 289 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
290 290 s: welcome to the server\n
291 291 s: if you find any issues, email someone@somewhere.com\n
292 292 s: 324\n
293 293 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
294 294 s: 1\n
295 295 s: \n
296 296
297 297 Note that output from the ``hello`` command is terminated by a ``\n``. This is
298 298 part of the response payload and not part of the wire protocol adding a newline
299 299 after responses. In other words, the length of the response contains the
300 300 trailing ``\n``.
301 301
302 302 Clients supporting version 2 of the SSH transport send a line beginning
303 303 with ``upgrade`` before the ``hello`` and ``between`` commands. The line
304 304 (which isn't a well-formed command line because it doesn't consist of a
305 305 single command name) serves to both communicate the client's intent to
306 306 switch to transport version 2 (transports are version 1 by default) as
307 307 well as to advertise the client's transport-level capabilities so the
308 308 server may satisfy that request immediately.
309 309
310 310 The upgrade line has the form:
311 311
312 312 upgrade <token> <transport capabilities>
313 313
314 314 That is the literal string ``upgrade`` followed by a space, followed by
315 315 a randomly generated string, followed by a space, followed by a string
316 316 denoting the client's transport capabilities.
317 317
318 318 The token can be anything. However, a random UUID is recommended. (Use
319 319 of version 4 UUIDs is recommended because version 1 UUIDs can leak the
320 320 client's MAC address.)
321 321
322 322 The transport capabilities string is a URL/percent encoded string
323 323 containing key-value pairs defining the client's transport-level
324 324 capabilities. The following capabilities are defined:
325 325
326 326 proto
327 327 A comma-delimited list of transport protocol versions the client
328 328 supports. e.g. ``ssh-v2``.
329 329
330 330 If the server does not recognize the ``upgrade`` line, it should issue
331 331 an empty response and continue processing the ``hello`` and ``between``
332 332 commands. Here is an example handshake between a version 2 aware client
333 333 and a non version 2 aware server:
334 334
335 335 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
336 336 c: hello\n
337 337 c: between\n
338 338 c: pairs 81\n
339 339 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
340 340 s: 0\n
341 341 s: 324\n
342 342 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
343 343 s: 1\n
344 344 s: \n
345 345
346 346 (The initial ``0\n`` line from the server indicates an empty response to
347 347 the unknown ``upgrade ..`` command/line.)
348 348
349 349 If the server recognizes the ``upgrade`` line and is willing to satisfy that
350 350 upgrade request, it replies to with a payload of the following form:
351 351
352 352 upgraded <token> <transport name>\n
353 353
354 354 This line is the literal string ``upgraded``, a space, the token that was
355 355 specified by the client in its ``upgrade ...`` request line, a space, and the
356 356 name of the transport protocol that was chosen by the server. The transport
357 357 name MUST match one of the names the client specified in the ``proto`` field
358 358 of its ``upgrade ...`` request line.
359 359
360 360 If a server issues an ``upgraded`` response, it MUST also read and ignore
361 361 the lines associated with the ``hello`` and ``between`` command requests
362 362 that were issued by the server. It is assumed that the negotiated transport
363 363 will respond with equivalent requested information following the transport
364 364 handshake.
365 365
366 366 All data following the ``\n`` terminating the ``upgraded`` line is the
367 367 domain of the negotiated transport. It is common for the data immediately
368 368 following to contain additional metadata about the state of the transport and
369 369 the server. However, this isn't strictly speaking part of the transport
370 370 handshake and isn't covered by this section.
371 371
372 372 Here is an example handshake between a version 2 aware client and a version
373 373 2 aware server:
374 374
375 375 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
376 376 c: hello\n
377 377 c: between\n
378 378 c: pairs 81\n
379 379 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
380 380 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
381 381 s: <additional transport specific data>
382 382
383 383 The client-issued token that is echoed in the response provides a more
384 384 resilient mechanism for differentiating *banner* output from Mercurial
385 385 output. In version 1, properly formatted banner output could get confused
386 386 for Mercurial server output. By submitting a randomly generated token
387 387 that is then present in the response, the client can look for that token
388 388 in response lines and have reasonable certainty that the line did not
389 389 originate from a *banner* message.
390 390
391 391 SSH Version 1 Transport
392 392 -----------------------
393 393
394 394 The SSH transport (version 1) is a custom text-based protocol suitable for
395 395 use over any bi-directional stream transport. It is most commonly used with
396 396 SSH.
397 397
398 398 A SSH transport server can be started with ``hg serve --stdio``. The stdin,
399 399 stderr, and stdout file descriptors of the started process are used to exchange
400 400 data. When Mercurial connects to a remote server over SSH, it actually starts
401 401 a ``hg serve --stdio`` process on the remote server.
402 402
403 403 Commands are issued by sending the command name followed by a trailing newline
404 404 ``\n`` to the server. e.g. ``capabilities\n``.
405 405
406 406 Command arguments are sent in the following format::
407 407
408 408 <argument> <length>\n<value>
409 409
410 410 That is, the argument string name followed by a space followed by the
411 411 integer length of the value (expressed as a string) followed by a newline
412 412 (``\n``) followed by the raw argument value.
413 413
414 414 Dictionary arguments are encoded differently::
415 415
416 416 <argument> <# elements>\n
417 417 <key1> <length1>\n<value1>
418 418 <key2> <length2>\n<value2>
419 419 ...
420 420
421 421 Non-argument data is sent immediately after the final argument value. It is
422 422 encoded in chunks::
423 423
424 424 <length>\n<data>
425 425
426 426 Each command declares a list of supported arguments and their types. If a
427 427 client sends an unknown argument to the server, the server should abort
428 428 immediately. The special argument ``*`` in a command's definition indicates
429 429 that all argument names are allowed.
430 430
431 431 The definition of supported arguments and types is initially made when a
432 432 new command is implemented. The client and server must initially independently
433 433 agree on the arguments and their types. This initial set of arguments can be
434 434 supplemented through the presence of *capabilities* advertised by the server.
435 435
436 436 Each command has a defined expected response type.
437 437
438 438 A ``string`` response type is a length framed value. The response consists of
439 439 the string encoded integer length of a value followed by a newline (``\n``)
440 440 followed by the value. Empty values are allowed (and are represented as
441 441 ``0\n``).
442 442
443 443 A ``stream`` response type consists of raw bytes of data. There is no framing.
444 444
445 445 A generic error response type is also supported. It consists of a an error
446 446 message written to ``stderr`` followed by ``\n-\n``. In addition, ``\n`` is
447 447 written to ``stdout``.
448 448
449 449 If the server receives an unknown command, it will send an empty ``string``
450 450 response.
451 451
452 452 The server terminates if it receives an empty command (a ``\n`` character).
453 453
454 454 If the server announces support for the ``protocaps`` capability, the client
455 455 should issue a ``protocaps`` command after the initial handshake to annonunce
456 456 its own capabilities. The client capabilities are persistent.
457 457
458 458 SSH Version 2 Transport
459 459 -----------------------
460 460
461 461 **Experimental and under development**
462 462
463 463 Version 2 of the SSH transport behaves identically to version 1 of the SSH
464 464 transport with the exception of handshake semantics. See above for how
465 465 version 2 of the SSH transport is negotiated.
466 466
467 467 Immediately following the ``upgraded`` line signaling a switch to version
468 468 2 of the SSH protocol, the server automatically sends additional details
469 469 about the capabilities of the remote server. This has the form:
470 470
471 471 <integer length of value>\n
472 472 capabilities: ...\n
473 473
474 474 e.g.
475 475
476 476 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
477 477 s: 240\n
478 478 s: capabilities: known getbundle batch ...\n
479 479
480 480 Following capabilities advertisement, the peers communicate using version
481 481 1 of the SSH transport.
482 482
483 483 Unified Frame-Based Protocol
484 484 ============================
485 485
486 486 **Experimental and under development**
487 487
488 488 The *Unified Frame-Based Protocol* is a communications protocol between
489 489 Mercurial peers. The protocol aims to be mostly transport agnostic
490 490 (works similarly on HTTP, SSH, etc).
491 491
492 492 To operate the protocol, a bi-directional, half-duplex pipe supporting
493 493 ordered sends and receives is required. That is, each peer has one pipe
494 494 for sending data and another for receiving.
495 495
496 496 All data is read and written in atomic units called *frames*. These
497 497 are conceptually similar to TCP packets. Higher-level functionality
498 498 is built on the exchange and processing of frames.
499 499
500 500 All frames are associated with a *stream*. A *stream* provides a
501 501 unidirectional grouping of frames. Streams facilitate two goals:
502 502 content encoding and parallelism. There is a dedicated section on
503 503 streams below.
504 504
505 505 The protocol is request-response based: the client issues requests to
506 506 the server, which issues replies to those requests. Server-initiated
507 507 messaging is not currently supported, but this specification carves
508 508 out room to implement it.
509 509
510 510 All frames are associated with a numbered request. Frames can thus
511 511 be logically grouped by their request ID.
512 512
513 513 Frames begin with an 8 octet header followed by a variable length
514 514 payload::
515 515
516 516 +------------------------------------------------+
517 517 | Length (24) |
518 518 +--------------------------------+---------------+
519 519 | Request ID (16) | Stream ID (8) |
520 520 +------------------+-------------+---------------+
521 521 | Stream Flags (8) |
522 522 +-----------+------+
523 523 | Type (4) |
524 524 +-----------+
525 525 | Flags (4) |
526 526 +===========+===================================================|
527 527 | Frame Payload (0...) ...
528 528 +---------------------------------------------------------------+
529 529
530 530 The length of the frame payload is expressed as an unsigned 24 bit
531 531 little endian integer. Values larger than 65535 MUST NOT be used unless
532 532 given permission by the server as part of the negotiated capabilities
533 533 during the handshake. The frame header is not part of the advertised
534 534 frame length. The payload length is the over-the-wire length. If there
535 535 is content encoding applied to the payload as part of the frame's stream,
536 536 the length is the output of that content encoding, not the input.
537 537
538 538 The 16-bit ``Request ID`` field denotes the integer request identifier,
539 539 stored as an unsigned little endian integer. Odd numbered requests are
540 540 client-initiated. Even numbered requests are server-initiated. This
541 541 refers to where the *request* was initiated - not where the *frame* was
542 542 initiated, so servers will send frames with odd ``Request ID`` in
543 543 response to client-initiated requests. Implementations are advised to
544 544 start ordering request identifiers at ``1`` and ``0``, increment by
545 545 ``2``, and wrap around if all available numbers have been exhausted.
546 546
547 547 The 8-bit ``Stream ID`` field denotes the stream that the frame is
548 548 associated with. Frames belonging to a stream may have content
549 549 encoding applied and the receiver may need to decode the raw frame
550 550 payload to obtain the original data. Odd numbered IDs are
551 551 client-initiated. Even numbered IDs are server-initiated.
552 552
553 553 The 8-bit ``Stream Flags`` field defines stream processing semantics.
554 554 See the section on streams below.
555 555
556 556 The 4-bit ``Type`` field denotes the type of frame being sent.
557 557
558 558 The 4-bit ``Flags`` field defines special, per-type attributes for
559 559 the frame.
560 560
561 561 The sections below define the frame types and their behavior.
562 562
563 563 Command Request (``0x01``)
564 564 --------------------------
565 565
566 566 This frame contains a request to run a command.
567 567
568 568 The payload consists of a CBOR map defining the command request. The
569 569 bytestring keys of that map are:
570 570
571 571 name
572 572 Name of the command that should be executed (bytestring).
573 573 args
574 574 Map of bytestring keys to various value types containing the named
575 575 arguments to this command.
576 576
577 577 Each command defines its own set of argument names and their expected
578 578 types.
579 579
580 580 This frame type MUST ONLY be sent from clients to servers: it is illegal
581 581 for a server to send this frame to a client.
582 582
583 583 The following flag values are defined for this type:
584 584
585 585 0x01
586 586 New command request. When set, this frame represents the beginning
587 587 of a new request to run a command. The ``Request ID`` attached to this
588 588 frame MUST NOT be active.
589 589 0x02
590 590 Command request continuation. When set, this frame is a continuation
591 591 from a previous command request frame for its ``Request ID``. This
592 592 flag is set when the CBOR data for a command request does not fit
593 593 in a single frame.
594 594 0x04
595 595 Additional frames expected. When set, the command request didn't fit
596 596 into a single frame and additional CBOR data follows in a subsequent
597 597 frame.
598 598 0x08
599 599 Command data frames expected. When set, command data frames are
600 600 expected to follow the final command request frame for this request.
601 601
602 602 ``0x01`` MUST be set on the initial command request frame for a
603 603 ``Request ID``.
604 604
605 605 ``0x01`` or ``0x02`` MUST be set to indicate this frame's role in
606 606 a series of command request frames.
607 607
608 608 If command data frames are to be sent, ``0x10`` MUST be set on ALL
609 609 command request frames.
610 610
611 611 Command Data (``0x03``)
612 612 -----------------------
613 613
614 614 This frame contains raw data for a command.
615 615
616 616 Most commands can be executed by specifying arguments. However,
617 617 arguments have an upper bound to their length. For commands that
618 618 accept data that is beyond this length or whose length isn't known
619 619 when the command is initially sent, they will need to stream
620 620 arbitrary data to the server. This frame type facilitates the sending
621 621 of this data.
622 622
623 623 The payload of this frame type consists of a stream of raw data to be
624 624 consumed by the command handler on the server. The format of the data
625 625 is command specific.
626 626
627 627 The following flag values are defined for this type:
628 628
629 629 0x01
630 630 Command data continuation. When set, the data for this command
631 631 continues into a subsequent frame.
632 632
633 633 0x02
634 634 End of data. When set, command data has been fully sent to the
635 635 server. The command has been fully issued and no new data for this
636 636 command will be sent. The next frame will belong to a new command.
637 637
638 638 Response Data (``0x04``)
639 639 ------------------------
640 640
641 641 This frame contains raw response data to an issued command.
642 642
643 643 The following flag values are defined for this type:
644 644
645 645 0x01
646 646 Data continuation. When set, an additional frame containing response data
647 647 will follow.
648 648 0x02
649 649 End of data. When set, the response data has been fully sent and
650 650 no additional frames for this response will be sent.
651 651 0x04
652 652 CBOR data. When set, the frame payload consists of CBOR data.
653 653
654 654 The ``0x01`` flag is mutually exclusive with the ``0x02`` flag.
655 655
656 656 Error Response (``0x05``)
657 657 -------------------------
658 658
659 659 An error occurred when processing a request. This could indicate
660 660 a protocol-level failure or an application level failure depending
661 661 on the flags for this message type.
662 662
663 663 The payload for this type is an error message that should be
664 664 displayed to the user.
665 665
666 666 The following flag values are defined for this type:
667 667
668 668 0x01
669 669 The error occurred at the transport/protocol level. If set, the
670 670 connection should be closed.
671 671 0x02
672 672 The error occurred at the application level. e.g. invalid command.
673 673
674 674 Human Output Side-Channel (``0x06``)
675 675 ------------------------------------
676 676
677 677 This frame contains a message that is intended to be displayed to
678 678 people. Whereas most frames communicate machine readable data, this
679 679 frame communicates textual data that is intended to be shown to
680 680 humans.
681 681
682 682 The frame consists of a series of *formatting requests*. Each formatting
683 683 request consists of a formatting string, arguments for that formatting
684 684 string, and labels to apply to that formatting string.
685 685
686 686 A formatting string is a printf()-like string that allows variable
687 687 substitution within the string. Labels allow the rendered text to be
688 688 *decorated*. Assuming use of the canonical Mercurial code base, a
689 689 formatting string can be the input to the ``i18n._`` function. This
690 690 allows messages emitted from the server to be localized. So even if
691 691 the server has different i18n settings, people could see messages in
692 692 their *native* settings. Similarly, the use of labels allows
693 693 decorations like coloring and underlining to be applied using the
694 694 client's configured rendering settings.
695 695
696 696 Formatting strings are similar to ``printf()`` strings or how
697 697 Python's ``%`` operator works. The only supported formatting sequences
698 698 are ``%s`` and ``%%``. ``%s`` will be replaced by whatever the string
699 699 at that position resolves to. ``%%`` will be replaced by ``%``. All
700 700 other 2-byte sequences beginning with ``%`` represent a literal
701 701 ``%`` followed by that character. However, future versions of the
702 702 wire protocol reserve the right to allow clients to opt in to receiving
703 703 formatting strings with additional formatters, hence why ``%%`` is
704 704 required to represent the literal ``%``.
705 705
706 706 The frame payload consists of a CBOR array of CBOR maps. Each map
707 707 defines an *atom* of text data to print. Each *atom* has the following
708 708 bytestring keys:
709 709
710 710 msg
711 711 (bytestring) The formatting string. Content MUST be ASCII.
712 712 args (optional)
713 713 Array of bytestrings defining arguments to the formatting string.
714 714 labels (optional)
715 715 Array of bytestrings defining labels to apply to this atom.
716 716
717 717 All data to be printed MUST be encoded into a single frame: this frame
718 718 does not support spanning data across multiple frames.
719 719
720 720 All textual data encoded in these frames is assumed to be line delimited.
721 721 The last atom in the frame SHOULD end with a newline (``\n``). If it
722 722 doesn't, clients MAY add a newline to facilitate immediate printing.
723 723
724 724 Progress Update (``0x07``)
725 725 --------------------------
726 726
727 727 This frame holds the progress of an operation on the peer. Consumption
728 728 of these frames allows clients to display progress bars, estimated
729 729 completion times, etc.
730 730
731 731 Each frame defines the progress of a single operation on the peer. The
732 732 payload consists of a CBOR map with the following bytestring keys:
733 733
734 734 topic
735 735 Topic name (string)
736 736 pos
737 737 Current numeric position within the topic (integer)
738 738 total
739 739 Total/end numeric position of this topic (unsigned integer)
740 740 label (optional)
741 741 Unit label (string)
742 742 item (optional)
743 743 Item name (string)
744 744
745 745 Progress state is created when a frame is received referencing a
746 746 *topic* that isn't currently tracked. Progress tracking for that
747 747 *topic* is finished when a frame is received reporting the current
748 748 position of that topic as ``-1``.
749 749
750 750 Multiple *topics* may be active at any given time.
751 751
752 752 Rendering of progress information is not mandated or governed by this
753 753 specification: implementations MAY render progress information however
754 754 they see fit, including not at all.
755 755
756 756 The string data describing the topic SHOULD be static strings to
757 757 facilitate receivers localizing that string data. The emitter
758 758 MUST normalize all string data to valid UTF-8 and receivers SHOULD
759 759 validate that received data conforms to UTF-8. The topic name
760 760 SHOULD be ASCII.
761 761
762 762 Stream Encoding Settings (``0x08``)
763 763 -----------------------------------
764 764
765 765 This frame type holds information defining the content encoding
766 766 settings for a *stream*.
767 767
768 768 This frame type is likely consumed by the protocol layer and is not
769 769 passed on to applications.
770 770
771 771 This frame type MUST ONLY occur on frames having the *Beginning of Stream*
772 772 ``Stream Flag`` set.
773 773
774 774 The payload of this frame defines what content encoding has (possibly)
775 775 been applied to the payloads of subsequent frames in this stream.
776 776
777 777 The payload begins with an 8-bit integer defining the length of the
778 778 encoding *profile*, followed by the string name of that profile, which
779 779 must be an ASCII string. All bytes that follow can be used by that
780 780 profile for supplemental settings definitions. See the section below
781 781 on defined encoding profiles.
782 782
783 783 Stream States and Flags
784 784 -----------------------
785 785
786 786 Streams can be in two states: *open* and *closed*. An *open* stream
787 787 is active and frames attached to that stream could arrive at any time.
788 788 A *closed* stream is not active. If a frame attached to a *closed*
789 789 stream arrives, that frame MUST have an appropriate stream flag
790 790 set indicating beginning of stream. All streams are in the *closed*
791 791 state by default.
792 792
793 793 The ``Stream Flags`` field denotes a set of bit flags for defining
794 794 the relationship of this frame within a stream. The following flags
795 795 are defined:
796 796
797 797 0x01
798 798 Beginning of stream. The first frame in the stream MUST set this
799 799 flag. When received, the ``Stream ID`` this frame is attached to
800 800 becomes ``open``.
801 801
802 802 0x02
803 803 End of stream. The last frame in a stream MUST set this flag. When
804 804 received, the ``Stream ID`` this frame is attached to becomes
805 805 ``closed``. Any content encoding context associated with this stream
806 806 can be destroyed after processing the payload of this frame.
807 807
808 808 0x04
809 809 Apply content encoding. When set, any content encoding settings
810 810 defined by the stream should be applied when attempting to read
811 811 the frame. When not set, the frame payload isn't encoded.
812 812
813 813 Streams
814 814 -------
815 815
816 816 Streams - along with ``Request IDs`` - facilitate grouping of frames.
817 817 But the purpose of each is quite different and the groupings they
818 818 constitute are independent.
819 819
820 820 A ``Request ID`` is essentially a tag. It tells you which logical
821 821 request a frame is associated with.
822 822
823 823 A *stream* is a sequence of frames grouped for the express purpose
824 824 of applying a stateful encoding or for denoting sub-groups of frames.
825 825
826 826 Unlike ``Request ID``s which span the request and response, a stream
827 827 is unidirectional and stream IDs are independent from client to
828 828 server.
829 829
830 830 There is no strict hierarchical relationship between ``Request IDs``
831 831 and *streams*. A stream can contain frames having multiple
832 832 ``Request IDs``. Frames belonging to the same ``Request ID`` can
833 833 span multiple streams.
834 834
835 835 One goal of streams is to facilitate content encoding. A stream can
836 836 define an encoding to be applied to frame payloads. For example, the
837 837 payload transmitted over the wire may contain output from a
838 838 zstandard compression operation and the receiving end may decompress
839 839 that payload to obtain the original data.
840 840
841 841 The other goal of streams is to facilitate concurrent execution. For
842 842 example, a server could spawn 4 threads to service a request that can
843 843 be easily parallelized. Each of those 4 threads could write into its
844 844 own stream. Those streams could then in turn be delivered to 4 threads
845 845 on the receiving end, with each thread consuming its stream in near
846 846 isolation. The *main* thread on both ends merely does I/O and
847 847 encodes/decodes frame headers: the bulk of the work is done by worker
848 848 threads.
849 849
850 850 In addition, since content encoding is defined per stream, each
851 851 *worker thread* could perform potentially CPU bound work concurrently
852 852 with other threads. This approach of applying encoding at the
853 853 sub-protocol / stream level eliminates a potential resource constraint
854 854 on the protocol stream as a whole (it is common for the throughput of
855 855 a compression engine to be smaller than the throughput of a network).
856 856
857 857 Having multiple streams - each with their own encoding settings - also
858 858 facilitates the use of advanced data compression techniques. For
859 859 example, a transmitter could see that it is generating data faster
860 860 and slower than the receiving end is consuming it and adjust its
861 861 compression settings to trade CPU for compression ratio accordingly.
862 862
863 863 While streams can define a content encoding, not all frames within
864 864 that stream must use that content encoding. This can be useful when
865 865 data is being served from caches and being derived dynamically. A
866 866 cache could pre-compressed data so the server doesn't have to
867 867 recompress it. The ability to pick and choose which frames are
868 868 compressed allows servers to easily send data to the wire without
869 869 involving potentially expensive encoding overhead.
870 870
871 871 Content Encoding Profiles
872 872 -------------------------
873 873
874 874 Streams can have named content encoding *profiles* associated with
875 875 them. A profile defines a shared understanding of content encoding
876 876 settings and behavior.
877 877
878 878 The following profiles are defined:
879 879
880 880 TBD
881 881
882 882 Issuing Commands
883 883 ----------------
884 884
885 885 A client can request that a remote run a command by sending it
886 886 frames defining that command. This logical stream is composed of
887 887 1 or more ``Command Request`` frames and and 0 or more ``Command Data``
888 888 frames.
889 889
890 890 All frames composing a single command request MUST be associated with
891 891 the same ``Request ID``.
892 892
893 893 Clients MAY send additional command requests without waiting on the
894 894 response to a previous command request. If they do so, they MUST ensure
895 895 that the ``Request ID`` field of outbound frames does not conflict
896 896 with that of an active ``Request ID`` whose response has not yet been
897 897 fully received.
898 898
899 899 Servers MAY respond to commands in a different order than they were
900 900 sent over the wire. Clients MUST be prepared to deal with this. Servers
901 901 also MAY start executing commands in a different order than they were
902 902 received, or MAY execute multiple commands concurrently.
903 903
904 904 If there is a dependency between commands or a race condition between
905 905 commands executing (e.g. a read-only command that depends on the results
906 906 of a command that mutates the repository), then clients MUST NOT send
907 907 frames issuing a command until a response to all dependent commands has
908 908 been received.
909 909 TODO think about whether we should express dependencies between commands
910 910 to avoid roundtrip latency.
911 911
912 912 A command is defined by a command name, 0 or more command arguments,
913 913 and optional command data.
914 914
915 915 Arguments are the recommended mechanism for transferring fixed sets of
916 916 parameters to a command. Data is appropriate for transferring variable
917 917 data. Thinking in terms of HTTP, arguments would be headers and data
918 918 would be the message body.
919 919
920 920 It is recommended for servers to delay the dispatch of a command
921 921 until all argument have been received. Servers MAY impose limits on the
922 922 maximum argument size.
923 923 TODO define failure mechanism.
924 924
925 925 Servers MAY dispatch to commands immediately once argument data
926 926 is available or delay until command data is received in full.
927 927
928 928 Capabilities
929 929 ============
930 930
931 931 Servers advertise supported wire protocol features. This allows clients to
932 932 probe for server features before blindly calling a command or passing a
933 933 specific argument.
934 934
935 935 The server's features are exposed via a *capabilities* string. This is a
936 936 space-delimited string of tokens/features. Some features are single words
937 937 like ``lookup`` or ``batch``. Others are complicated key-value pairs
938 938 advertising sub-features. e.g. ``httpheader=2048``. When complex, non-word
939 939 values are used, each feature name can define its own encoding of sub-values.
940 940 Comma-delimited and ``x-www-form-urlencoded`` values are common.
941 941
942 942 The following document capabilities defined by the canonical Mercurial server
943 943 implementation.
944 944
945 945 batch
946 946 -----
947 947
948 948 Whether the server supports the ``batch`` command.
949 949
950 950 This capability/command was introduced in Mercurial 1.9 (released July 2011).
951 951
952 952 branchmap
953 953 ---------
954 954
955 955 Whether the server supports the ``branchmap`` command.
956 956
957 957 This capability/command was introduced in Mercurial 1.3 (released July 2009).
958 958
959 959 bundle2-exp
960 960 -----------
961 961
962 962 Precursor to ``bundle2`` capability that was used before bundle2 was a
963 963 stable feature.
964 964
965 965 This capability was introduced in Mercurial 3.0 behind an experimental
966 966 flag. This capability should not be observed in the wild.
967 967
968 968 bundle2
969 969 -------
970 970
971 971 Indicates whether the server supports the ``bundle2`` data exchange format.
972 972
973 973 The value of the capability is a URL quoted, newline (``\n``) delimited
974 974 list of keys or key-value pairs.
975 975
976 976 A key is simply a URL encoded string.
977 977
978 978 A key-value pair is a URL encoded key separated from a URL encoded value by
979 979 an ``=``. If the value is a list, elements are delimited by a ``,`` after
980 980 URL encoding.
981 981
982 982 For example, say we have the values::
983 983
984 984 {'HG20': [], 'changegroup': ['01', '02'], 'digests': ['sha1', 'sha512']}
985 985
986 986 We would first construct a string::
987 987
988 988 HG20\nchangegroup=01,02\ndigests=sha1,sha512
989 989
990 990 We would then URL quote this string::
991 991
992 992 HG20%0Achangegroup%3D01%2C02%0Adigests%3Dsha1%2Csha512
993 993
994 994 This capability was introduced in Mercurial 3.4 (released May 2015).
995 995
996 996 changegroupsubset
997 997 -----------------
998 998
999 999 Whether the server supports the ``changegroupsubset`` command.
1000 1000
1001 1001 This capability was introduced in Mercurial 0.9.2 (released December
1002 1002 2006).
1003 1003
1004 1004 This capability was introduced at the same time as the ``lookup``
1005 1005 capability/command.
1006 1006
1007 1007 compression
1008 1008 -----------
1009 1009
1010 1010 Declares support for negotiating compression formats.
1011 1011
1012 1012 Presence of this capability indicates the server supports dynamic selection
1013 1013 of compression formats based on the client request.
1014 1014
1015 1015 Servers advertising this capability are required to support the
1016 1016 ``application/mercurial-0.2`` media type in response to commands returning
1017 1017 streams. Servers may support this media type on any command.
1018 1018
1019 1019 The value of the capability is a comma-delimited list of strings declaring
1020 1020 supported compression formats. The order of the compression formats is in
1021 1021 server-preferred order, most preferred first.
1022 1022
1023 1023 The identifiers used by the official Mercurial distribution are:
1024 1024
1025 1025 bzip2
1026 1026 bzip2
1027 1027 none
1028 1028 uncompressed / raw data
1029 1029 zlib
1030 1030 zlib (no gzip header)
1031 1031 zstd
1032 1032 zstd
1033 1033
1034 1034 This capability was introduced in Mercurial 4.1 (released February 2017).
1035 1035
1036 1036 getbundle
1037 1037 ---------
1038 1038
1039 1039 Whether the server supports the ``getbundle`` command.
1040 1040
1041 1041 This capability was introduced in Mercurial 1.9 (released July 2011).
1042 1042
1043 1043 httpheader
1044 1044 ----------
1045 1045
1046 1046 Whether the server supports receiving command arguments via HTTP request
1047 1047 headers.
1048 1048
1049 1049 The value of the capability is an integer describing the max header
1050 1050 length that clients should send. Clients should ignore any content after a
1051 1051 comma in the value, as this is reserved for future use.
1052 1052
1053 1053 This capability was introduced in Mercurial 1.9 (released July 2011).
1054 1054
1055 1055 httpmediatype
1056 1056 -------------
1057 1057
1058 1058 Indicates which HTTP media types (``Content-Type`` header) the server is
1059 1059 capable of receiving and sending.
1060 1060
1061 1061 The value of the capability is a comma-delimited list of strings identifying
1062 1062 support for media type and transmission direction. The following strings may
1063 1063 be present:
1064 1064
1065 1065 0.1rx
1066 1066 Indicates server support for receiving ``application/mercurial-0.1`` media
1067 1067 types.
1068 1068
1069 1069 0.1tx
1070 1070 Indicates server support for sending ``application/mercurial-0.1`` media
1071 1071 types.
1072 1072
1073 1073 0.2rx
1074 1074 Indicates server support for receiving ``application/mercurial-0.2`` media
1075 1075 types.
1076 1076
1077 1077 0.2tx
1078 1078 Indicates server support for sending ``application/mercurial-0.2`` media
1079 1079 types.
1080 1080
1081 1081 minrx=X
1082 1082 Minimum media type version the server is capable of receiving. Value is a
1083 1083 string like ``0.2``.
1084 1084
1085 1085 This capability can be used by servers to limit connections from legacy
1086 1086 clients not using the latest supported media type. However, only clients
1087 1087 with knowledge of this capability will know to consult this value. This
1088 1088 capability is present so the client may issue a more user-friendly error
1089 1089 when the server has locked out a legacy client.
1090 1090
1091 1091 mintx=X
1092 1092 Minimum media type version the server is capable of sending. Value is a
1093 1093 string like ``0.1``.
1094 1094
1095 1095 Servers advertising support for the ``application/mercurial-0.2`` media type
1096 1096 should also advertise the ``compression`` capability.
1097 1097
1098 1098 This capability was introduced in Mercurial 4.1 (released February 2017).
1099 1099
1100 1100 httppostargs
1101 1101 ------------
1102 1102
1103 1103 **Experimental**
1104 1104
1105 1105 Indicates that the server supports and prefers clients send command arguments
1106 1106 via a HTTP POST request as part of the request body.
1107 1107
1108 1108 This capability was introduced in Mercurial 3.8 (released May 2016).
1109 1109
1110 1110 known
1111 1111 -----
1112 1112
1113 1113 Whether the server supports the ``known`` command.
1114 1114
1115 1115 This capability/command was introduced in Mercurial 1.9 (released July 2011).
1116 1116
1117 1117 lookup
1118 1118 ------
1119 1119
1120 1120 Whether the server supports the ``lookup`` command.
1121 1121
1122 1122 This capability was introduced in Mercurial 0.9.2 (released December
1123 1123 2006).
1124 1124
1125 1125 This capability was introduced at the same time as the ``changegroupsubset``
1126 1126 capability/command.
1127 1127
1128 1128 partial-pull
1129 1129 ------------
1130 1130
1131 1131 Indicates that the client can deal with partial answers to pull requests
1132 1132 by repeating the request.
1133 1133
1134 1134 If this parameter is not advertised, the server will not send pull bundles.
1135 1135
1136 1136 This client capability was introduced in Mercurial 4.6.
1137 1137
1138 1138 protocaps
1139 1139 ---------
1140 1140
1141 1141 Whether the server supports the ``protocaps`` command for SSH V1 transport.
1142 1142
1143 1143 This capability was introduced in Mercurial 4.6.
1144 1144
1145 1145 pushkey
1146 1146 -------
1147 1147
1148 1148 Whether the server supports the ``pushkey`` and ``listkeys`` commands.
1149 1149
1150 1150 This capability was introduced in Mercurial 1.6 (released July 2010).
1151 1151
1152 1152 standardbundle
1153 1153 --------------
1154 1154
1155 1155 **Unsupported**
1156 1156
1157 1157 This capability was introduced during the Mercurial 0.9.2 development cycle in
1158 1158 2006. It was never present in a release, as it was replaced by the ``unbundle``
1159 1159 capability. This capability should not be encountered in the wild.
1160 1160
1161 1161 stream-preferred
1162 1162 ----------------
1163 1163
1164 1164 If present the server prefers that clients clone using the streaming clone
1165 1165 protocol (``hg clone --stream``) rather than the standard
1166 1166 changegroup/bundle based protocol.
1167 1167
1168 1168 This capability was introduced in Mercurial 2.2 (released May 2012).
1169 1169
1170 1170 streamreqs
1171 1171 ----------
1172 1172
1173 1173 Indicates whether the server supports *streaming clones* and the *requirements*
1174 1174 that clients must support to receive it.
1175 1175
1176 1176 If present, the server supports the ``stream_out`` command, which transmits
1177 1177 raw revlogs from the repository instead of changegroups. This provides a faster
1178 1178 cloning mechanism at the expense of more bandwidth used.
1179 1179
1180 1180 The value of this capability is a comma-delimited list of repo format
1181 1181 *requirements*. These are requirements that impact the reading of data in
1182 1182 the ``.hg/store`` directory. An example value is
1183 1183 ``streamreqs=generaldelta,revlogv1`` indicating the server repo requires
1184 1184 the ``revlogv1`` and ``generaldelta`` requirements.
1185 1185
1186 1186 If the only format requirement is ``revlogv1``, the server may expose the
1187 1187 ``stream`` capability instead of the ``streamreqs`` capability.
1188 1188
1189 1189 This capability was introduced in Mercurial 1.7 (released November 2010).
1190 1190
1191 1191 stream
1192 1192 ------
1193 1193
1194 1194 Whether the server supports *streaming clones* from ``revlogv1`` repos.
1195 1195
1196 1196 If present, the server supports the ``stream_out`` command, which transmits
1197 1197 raw revlogs from the repository instead of changegroups. This provides a faster
1198 1198 cloning mechanism at the expense of more bandwidth used.
1199 1199
1200 1200 This capability was introduced in Mercurial 0.9.1 (released July 2006).
1201 1201
1202 1202 When initially introduced, the value of the capability was the numeric
1203 1203 revlog revision. e.g. ``stream=1``. This indicates the changegroup is using
1204 1204 ``revlogv1``. This simple integer value wasn't powerful enough, so the
1205 1205 ``streamreqs`` capability was invented to handle cases where the repo
1206 1206 requirements have more than just ``revlogv1``. Newer servers omit the
1207 1207 ``=1`` since it was the only value supported and the value of ``1`` can
1208 1208 be implied by clients.
1209 1209
1210 1210 unbundlehash
1211 1211 ------------
1212 1212
1213 1213 Whether the ``unbundle`` commands supports receiving a hash of all the
1214 1214 heads instead of a list.
1215 1215
1216 1216 For more, see the documentation for the ``unbundle`` command.
1217 1217
1218 1218 This capability was introduced in Mercurial 1.9 (released July 2011).
1219 1219
1220 1220 unbundle
1221 1221 --------
1222 1222
1223 1223 Whether the server supports pushing via the ``unbundle`` command.
1224 1224
1225 1225 This capability/command has been present since Mercurial 0.9.1 (released
1226 1226 July 2006).
1227 1227
1228 1228 Mercurial 0.9.2 (released December 2006) added values to the capability
1229 1229 indicating which bundle types the server supports receiving. This value is a
1230 1230 comma-delimited list. e.g. ``HG10GZ,HG10BZ,HG10UN``. The order of values
1231 1231 reflects the priority/preference of that type, where the first value is the
1232 1232 most preferred type.
1233 1233
1234 1234 Content Negotiation
1235 1235 ===================
1236 1236
1237 1237 The wire protocol has some mechanisms to help peers determine what content
1238 1238 types and encoding the other side will accept. Historically, these mechanisms
1239 1239 have been built into commands themselves because most commands only send a
1240 1240 well-defined response type and only certain commands needed to support
1241 1241 functionality like compression.
1242 1242
1243 1243 Currently, only the HTTP version 1 transport supports content negotiation
1244 1244 at the protocol layer.
1245 1245
1246 1246 HTTP requests advertise supported response formats via the ``X-HgProto-<N>``
1247 1247 request header, where ``<N>`` is an integer starting at 1 allowing the logical
1248 1248 value to span multiple headers. This value consists of a list of
1249 1249 space-delimited parameters. Each parameter denotes a feature or capability.
1250 1250
1251 1251 The following parameters are defined:
1252 1252
1253 1253 0.1
1254 1254 Indicates the client supports receiving ``application/mercurial-0.1``
1255 1255 responses.
1256 1256
1257 1257 0.2
1258 1258 Indicates the client supports receiving ``application/mercurial-0.2``
1259 1259 responses.
1260 1260
1261 1261 comp
1262 1262 Indicates compression formats the client can decode. Value is a list of
1263 1263 comma delimited strings identifying compression formats ordered from
1264 1264 most preferential to least preferential. e.g. ``comp=zstd,zlib,none``.
1265 1265
1266 1266 This parameter does not have an effect if only the ``0.1`` parameter
1267 1267 is defined, as support for ``application/mercurial-0.2`` or greater is
1268 1268 required to use arbitrary compression formats.
1269 1269
1270 1270 If this parameter is not advertised, the server interprets this as
1271 1271 equivalent to ``zlib,none``.
1272 1272
1273 1273 Clients may choose to only send this header if the ``httpmediatype``
1274 1274 server capability is present, as currently all server-side features
1275 1275 consulting this header require the client to opt in to new protocol features
1276 1276 advertised via the ``httpmediatype`` capability.
1277 1277
1278 1278 A server that doesn't receive an ``X-HgProto-<N>`` header should infer a
1279 1279 value of ``0.1``. This is compatible with legacy clients.
1280 1280
1281 1281 A server receiving a request indicating support for multiple media type
1282 1282 versions may respond with any of the supported media types. Not all servers
1283 1283 may support all media types on all commands.
1284 1284
1285 1285 Commands
1286 1286 ========
1287 1287
1288 1288 This section contains a list of all wire protocol commands implemented by
1289 1289 the canonical Mercurial server.
1290 1290
1291 1291 batch
1292 1292 -----
1293 1293
1294 1294 Issue multiple commands while sending a single command request. The purpose
1295 1295 of this command is to allow a client to issue multiple commands while avoiding
1296 1296 multiple round trips to the server therefore enabling commands to complete
1297 1297 quicker.
1298 1298
1299 1299 The command accepts a ``cmds`` argument that contains a list of commands to
1300 1300 execute.
1301 1301
1302 1302 The value of ``cmds`` is a ``;`` delimited list of strings. Each string has the
1303 1303 form ``<command> <arguments>``. That is, the command name followed by a space
1304 1304 followed by an argument string.
1305 1305
1306 1306 The argument string is a ``,`` delimited list of ``<key>=<value>`` values
1307 1307 corresponding to command arguments. Both the argument name and value are
1308 1308 escaped using a special substitution map::
1309 1309
1310 1310 : -> :c
1311 1311 , -> :o
1312 1312 ; -> :s
1313 1313 = -> :e
1314 1314
1315 1315 The response type for this command is ``string``. The value contains a
1316 1316 ``;`` delimited list of responses for each requested command. Each value
1317 1317 in this list is escaped using the same substitution map used for arguments.
1318 1318
1319 1319 If an error occurs, the generic error response may be sent.
1320 1320
1321 1321 between
1322 1322 -------
1323 1323
1324 1324 (Legacy command used for discovery in old clients)
1325 1325
1326 1326 Obtain nodes between pairs of nodes.
1327 1327
1328 1328 The ``pairs`` arguments contains a space-delimited list of ``-`` delimited
1329 1329 hex node pairs. e.g.::
1330 1330
1331 1331 a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896-6dc58916e7c070f678682bfe404d2e2d68291a18
1332 1332
1333 1333 Return type is a ``string``. Value consists of lines corresponding to each
1334 1334 requested range. Each line contains a space-delimited list of hex nodes.
1335 1335 A newline ``\n`` terminates each line, including the last one.
1336 1336
1337 1337 branchmap
1338 1338 ---------
1339 1339
1340 1340 Obtain heads in named branches.
1341 1341
1342 1342 Accepts no arguments. Return type is a ``string``.
1343 1343
1344 1344 Return value contains lines with URL encoded branch names followed by a space
1345 1345 followed by a space-delimited list of hex nodes of heads on that branch.
1346 1346 e.g.::
1347 1347
1348 1348 default a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896 6dc58916e7c070f678682bfe404d2e2d68291a18
1349 1349 stable baae3bf31522f41dd5e6d7377d0edd8d1cf3fccc
1350 1350
1351 1351 There is no trailing newline.
1352 1352
1353 1353 branches
1354 1354 --------
1355 1355
1356 1356 (Legacy command used for discovery in old clients. Clients with ``getbundle``
1357 1357 use the ``known`` and ``heads`` commands instead.)
1358 1358
1359 1359 Obtain ancestor changesets of specific nodes back to a branch point.
1360 1360
1361 1361 Despite the name, this command has nothing to do with Mercurial named branches.
1362 1362 Instead, it is related to DAG branches.
1363 1363
1364 1364 The command accepts a ``nodes`` argument, which is a string of space-delimited
1365 1365 hex nodes.
1366 1366
1367 1367 For each node requested, the server will find the first ancestor node that is
1368 1368 a DAG root or is a merge.
1369 1369
1370 1370 Return type is a ``string``. Return value contains lines with result data for
1371 1371 each requested node. Each line contains space-delimited nodes followed by a
1372 1372 newline (``\n``). The 4 nodes reported on each line correspond to the requested
1373 1373 node, the ancestor node found, and its 2 parent nodes (which may be the null
1374 1374 node).
1375 1375
1376 1376 capabilities
1377 1377 ------------
1378 1378
1379 1379 Obtain the capabilities string for the repo.
1380 1380
1381 1381 Unlike the ``hello`` command, the capabilities string is not prefixed.
1382 1382 There is no trailing newline.
1383 1383
1384 1384 This command does not accept any arguments. Return type is a ``string``.
1385 1385
1386 1386 This command was introduced in Mercurial 0.9.1 (released July 2006).
1387 1387
1388 1388 changegroup
1389 1389 -----------
1390 1390
1391 1391 (Legacy command: use ``getbundle`` instead)
1392 1392
1393 1393 Obtain a changegroup version 1 with data for changesets that are
1394 1394 descendants of client-specified changesets.
1395 1395
1396 1396 The ``roots`` arguments contains a list of space-delimited hex nodes.
1397 1397
1398 1398 The server responds with a changegroup version 1 containing all
1399 1399 changesets between the requested root/base nodes and the repo's head nodes
1400 1400 at the time of the request.
1401 1401
1402 1402 The return type is a ``stream``.
1403 1403
1404 1404 changegroupsubset
1405 1405 -----------------
1406 1406
1407 1407 (Legacy command: use ``getbundle`` instead)
1408 1408
1409 1409 Obtain a changegroup version 1 with data for changesetsets between
1410 1410 client specified base and head nodes.
1411 1411
1412 1412 The ``bases`` argument contains a list of space-delimited hex nodes.
1413 1413 The ``heads`` argument contains a list of space-delimited hex nodes.
1414 1414
1415 1415 The server responds with a changegroup version 1 containing all
1416 1416 changesets between the requested base and head nodes at the time of the
1417 1417 request.
1418 1418
1419 1419 The return type is a ``stream``.
1420 1420
1421 1421 clonebundles
1422 1422 ------------
1423 1423
1424 1424 Obtains a manifest of bundle URLs available to seed clones.
1425 1425
1426 1426 Each returned line contains a URL followed by metadata. See the
1427 1427 documentation in the ``clonebundles`` extension for more.
1428 1428
1429 1429 The return type is a ``string``.
1430 1430
1431 1431 getbundle
1432 1432 ---------
1433 1433
1434 1434 Obtain a bundle containing repository data.
1435 1435
1436 1436 This command accepts the following arguments:
1437 1437
1438 1438 heads
1439 1439 List of space-delimited hex nodes of heads to retrieve.
1440 1440 common
1441 1441 List of space-delimited hex nodes that the client has in common with the
1442 1442 server.
1443 1443 obsmarkers
1444 1444 Boolean indicating whether to include obsolescence markers as part
1445 1445 of the response. Only works with bundle2.
1446 1446 bundlecaps
1447 1447 Comma-delimited set of strings defining client bundle capabilities.
1448 1448 listkeys
1449 1449 Comma-delimited list of strings of ``pushkey`` namespaces. For each
1450 1450 namespace listed, a bundle2 part will be included with the content of
1451 1451 that namespace.
1452 1452 cg
1453 1453 Boolean indicating whether changegroup data is requested.
1454 1454 cbattempted
1455 1455 Boolean indicating whether the client attempted to use the *clone bundles*
1456 1456 feature before performing this request.
1457 1457 bookmarks
1458 1458 Boolean indicating whether bookmark data is requested.
1459 1459 phases
1460 1460 Boolean indicating whether phases data is requested.
1461 1461
1462 1462 The return type on success is a ``stream`` where the value is bundle.
1463 1463 On the HTTP version 1 transport, the response is zlib compressed.
1464 1464
1465 1465 If an error occurs, a generic error response can be sent.
1466 1466
1467 1467 Unless the client sends a false value for the ``cg`` argument, the returned
1468 1468 bundle contains a changegroup with the nodes between the specified ``common``
1469 1469 and ``heads`` nodes. Depending on the command arguments, the type and content
1470 1470 of the returned bundle can vary significantly.
1471 1471
1472 1472 The default behavior is for the server to send a raw changegroup version
1473 1473 ``01`` response.
1474 1474
1475 1475 If the ``bundlecaps`` provided by the client contain a value beginning
1476 1476 with ``HG2``, a bundle2 will be returned. The bundle2 data may contain
1477 1477 additional repository data, such as ``pushkey`` namespace values.
1478 1478
1479 1479 heads
1480 1480 -----
1481 1481
1482 1482 Returns a list of space-delimited hex nodes of repository heads followed
1483 1483 by a newline. e.g.
1484 1484 ``a9eeb3adc7ddb5006c088e9eda61791c777cbf7c 31f91a3da534dc849f0d6bfc00a395a97cf218a1\n``
1485 1485
1486 1486 This command does not accept any arguments. The return type is a ``string``.
1487 1487
1488 1488 hello
1489 1489 -----
1490 1490
1491 1491 Returns lines describing interesting things about the server in an RFC-822
1492 1492 like format.
1493 1493
1494 1494 Currently, the only line defines the server capabilities. It has the form::
1495 1495
1496 1496 capabilities: <value>
1497 1497
1498 1498 See above for more about the capabilities string.
1499 1499
1500 1500 SSH clients typically issue this command as soon as a connection is
1501 1501 established.
1502 1502
1503 1503 This command does not accept any arguments. The return type is a ``string``.
1504 1504
1505 1505 This command was introduced in Mercurial 0.9.1 (released July 2006).
1506 1506
1507 1507 listkeys
1508 1508 --------
1509 1509
1510 1510 List values in a specified ``pushkey`` namespace.
1511 1511
1512 1512 The ``namespace`` argument defines the pushkey namespace to operate on.
1513 1513
1514 1514 The return type is a ``string``. The value is an encoded dictionary of keys.
1515 1515
1516 1516 Key-value pairs are delimited by newlines (``\n``). Within each line, keys and
1517 1517 values are separated by a tab (``\t``). Keys and values are both strings.
1518 1518
1519 1519 lookup
1520 1520 ------
1521 1521
1522 1522 Try to resolve a value to a known repository revision.
1523 1523
1524 1524 The ``key`` argument is converted from bytes to an
1525 1525 ``encoding.localstr`` instance then passed into
1526 1526 ``localrepository.__getitem__`` in an attempt to resolve it.
1527 1527
1528 1528 The return type is a ``string``.
1529 1529
1530 1530 Upon successful resolution, returns ``1 <hex node>\n``. On failure,
1531 1531 returns ``0 <error string>\n``. e.g.::
1532 1532
1533 1533 1 273ce12ad8f155317b2c078ec75a4eba507f1fba\n
1534 1534
1535 1535 0 unknown revision 'foo'\n
1536 1536
1537 1537 known
1538 1538 -----
1539 1539
1540 1540 Determine whether multiple nodes are known.
1541 1541
1542 1542 The ``nodes`` argument is a list of space-delimited hex nodes to check
1543 1543 for existence.
1544 1544
1545 1545 The return type is ``string``.
1546 1546
1547 1547 Returns a string consisting of ``0``s and ``1``s indicating whether nodes
1548 1548 are known. If the Nth node specified in the ``nodes`` argument is known,
1549 1549 a ``1`` will be returned at byte offset N. If the node isn't known, ``0``
1550 1550 will be present at byte offset N.
1551 1551
1552 1552 There is no trailing newline.
1553 1553
1554 1554 protocaps
1555 1555 ---------
1556 1556
1557 1557 Notify the server about the client capabilities in the SSH V1 transport
1558 1558 protocol.
1559 1559
1560 1560 The ``caps`` argument is a space-delimited list of capabilities.
1561 1561
1562 1562 The server will reply with the string ``OK``.
1563 1563
1564 1564 pushkey
1565 1565 -------
1566 1566
1567 1567 Set a value using the ``pushkey`` protocol.
1568 1568
1569 1569 Accepts arguments ``namespace``, ``key``, ``old``, and ``new``, which
1570 1570 correspond to the pushkey namespace to operate on, the key within that
1571 1571 namespace to change, the old value (which may be empty), and the new value.
1572 1572 All arguments are string types.
1573 1573
1574 1574 The return type is a ``string``. The value depends on the transport protocol.
1575 1575
1576 1576 The SSH version 1 transport sends a string encoded integer followed by a
1577 1577 newline (``\n``) which indicates operation result. The server may send
1578 1578 additional output on the ``stderr`` stream that should be displayed to the
1579 1579 user.
1580 1580
1581 1581 The HTTP version 1 transport sends a string encoded integer followed by a
1582 1582 newline followed by additional server output that should be displayed to
1583 1583 the user. This may include output from hooks, etc.
1584 1584
1585 1585 The integer result varies by namespace. ``0`` means an error has occurred
1586 1586 and there should be additional output to display to the user.
1587 1587
1588 1588 stream_out
1589 1589 ----------
1590 1590
1591 1591 Obtain *streaming clone* data.
1592 1592
1593 1593 The return type is either a ``string`` or a ``stream``, depending on
1594 1594 whether the request was fulfilled properly.
1595 1595
1596 1596 A return value of ``1\n`` indicates the server is not configured to serve
1597 1597 this data. If this is seen by the client, they may not have verified the
1598 1598 ``stream`` capability is set before making the request.
1599 1599
1600 1600 A return value of ``2\n`` indicates the server was unable to lock the
1601 1601 repository to generate data.
1602 1602
1603 1603 All other responses are a ``stream`` of bytes. The first line of this data
1604 1604 contains 2 space-delimited integers corresponding to the path count and
1605 1605 payload size, respectively::
1606 1606
1607 1607 <path count> <payload size>\n
1608 1608
1609 1609 The ``<payload size>`` is the total size of path data: it does not include
1610 1610 the size of the per-path header lines.
1611 1611
1612 1612 Following that header are ``<path count>`` entries. Each entry consists of a
1613 1613 line with metadata followed by raw revlog data. The line consists of::
1614 1614
1615 1615 <store path>\0<size>\n
1616 1616
1617 1617 The ``<store path>`` is the encoded store path of the data that follows.
1618 1618 ``<size>`` is the amount of data for this store path/revlog that follows the
1619 1619 newline.
1620 1620
1621 1621 There is no trailer to indicate end of data. Instead, the client should stop
1622 1622 reading after ``<path count>`` entries are consumed.
1623 1623
1624 1624 unbundle
1625 1625 --------
1626 1626
1627 1627 Send a bundle containing data (usually changegroup data) to the server.
1628 1628
1629 1629 Accepts the argument ``heads``, which is a space-delimited list of hex nodes
1630 1630 corresponding to server repository heads observed by the client. This is used
1631 1631 to detect race conditions and abort push operations before a server performs
1632 1632 too much work or a client transfers too much data.
1633 1633
1634 1634 The request payload consists of a bundle to be applied to the repository,
1635 1635 similarly to as if :hg:`unbundle` were called.
1636 1636
1637 1637 In most scenarios, a special ``push response`` type is returned. This type
1638 1638 contains an integer describing the change in heads as a result of the
1639 1639 operation. A value of ``0`` indicates nothing changed. ``1`` means the number
1640 1640 of heads remained the same. Values ``2`` and larger indicate the number of
1641 1641 added heads minus 1. e.g. ``3`` means 2 heads were added. Negative values
1642 1642 indicate the number of fewer heads, also off by 1. e.g. ``-2`` means there
1643 1643 is 1 fewer head.
1644 1644
1645 1645 The encoding of the ``push response`` type varies by transport.
1646 1646
1647 1647 For the SSH version 1 transport, this type is composed of 2 ``string``
1648 1648 responses: an empty response (``0\n``) followed by the integer result value.
1649 1649 e.g. ``1\n2``. So the full response might be ``0\n1\n2``.
1650 1650
1651 1651 For the HTTP version 1 transport, the response is a ``string`` type composed
1652 1652 of an integer result value followed by a newline (``\n``) followed by string
1653 1653 content holding server output that should be displayed on the client (output
1654 1654 hooks, etc).
1655 1655
1656 1656 In some cases, the server may respond with a ``bundle2`` bundle. In this
1657 1657 case, the response type is ``stream``. For the HTTP version 1 transport, the
1658 1658 response is zlib compressed.
1659 1659
1660 1660 The server may also respond with a generic error type, which contains a string
1661 1661 indicating the failure.
1662 1662
1663 1663 Frame-Based Protocol Commands
1664 1664 =============================
1665 1665
1666 1666 **Experimental and under active development**
1667 1667
1668 1668 This section documents the wire protocol commands exposed to transports
1669 1669 using the frame-based protocol. The set of commands exposed through
1670 1670 these transports is distinct from the set of commands exposed to legacy
1671 1671 transports.
1672 1672
1673 1673 The frame-based protocol uses CBOR to encode command execution requests.
1674 1674 All command arguments must be mapped to a specific or set of CBOR data
1675 1675 types.
1676 1676
1677 1677 The response to many commands is also CBOR. There is no common response
1678 1678 format: each command defines its own response format.
1679 1679
1680 1680 TODO require node type be specified, as N bytes of binary node value
1681 1681 could be ambiguous once SHA-1 is replaced.
1682 1682
1683 1683 branchmap
1684 1684 ---------
1685 1685
1686 1686 Obtain heads in named branches.
1687 1687
1688 1688 Receives no arguments.
1689 1689
1690 1690 The response is a map with bytestring keys defining the branch name.
1691 1691 Values are arrays of bytestring defining raw changeset nodes.
1692 1692
1693 1693 capabilities
1694 1694 ------------
1695 1695
1696 1696 Obtain the server's capabilities.
1697 1697
1698 1698 Receives no arguments.
1699 1699
1700 1700 This command is typically called only as part of the handshake during
1701 1701 initial connection establishment.
1702 1702
1703 1703 The response is a map with bytestring keys defining server information.
1704 1704
1705 1705 The defined keys are:
1706 1706
1707 1707 commands
1708 1708 A map defining available wire protocol commands on this server.
1709 1709
1710 1710 Keys in the map are the names of commands that can be invoked. Values
1711 1711 are maps defining information about that command. The bytestring keys
1712 1712 are:
1713 1713
1714 1714 args
1715 An array of argument names accepted by this command.
1715 A map of argument names and their expected types.
1716
1717 Types are defined as a representative value for the expected type.
1718 e.g. an argument expecting a boolean type will have its value
1719 set to true. An integer type will have its value set to 42. The
1720 actual values are arbitrary and may not have meaning.
1716 1721 permissions
1717 1722 An array of permissions required to execute this command.
1718 1723
1719 1724 compression
1720 1725 An array of maps defining available compression format support.
1721 1726
1722 1727 The array is sorted from most preferred to least preferred.
1723 1728
1724 1729 Each entry has the following bytestring keys:
1725 1730
1726 1731 name
1727 1732 Name of the compression engine. e.g. ``zstd`` or ``zlib``.
1728 1733
1729 1734 heads
1730 1735 -----
1731 1736
1732 1737 Obtain DAG heads in the repository.
1733 1738
1734 1739 The command accepts the following arguments:
1735 1740
1736 1741 publiconly (optional)
1737 1742 (boolean) If set, operate on the DAG for public phase changesets only.
1738 1743 Non-public (i.e. draft) phase DAG heads will not be returned.
1739 1744
1740 1745 The response is a CBOR array of bytestrings defining changeset nodes
1741 1746 of DAG heads. The array can be empty if the repository is empty or no
1742 1747 changesets satisfied the request.
1743 1748
1744 1749 TODO consider exposing phase of heads in response
1745 1750
1746 1751 known
1747 1752 -----
1748 1753
1749 1754 Determine whether a series of changeset nodes is known to the server.
1750 1755
1751 1756 The command accepts the following arguments:
1752 1757
1753 1758 nodes
1754 1759 (array of bytestrings) List of changeset nodes whose presence to
1755 1760 query.
1756 1761
1757 1762 The response is a bytestring where each byte contains a 0 or 1 for the
1758 1763 corresponding requested node at the same index.
1759 1764
1760 1765 TODO use a bit array for even more compact response
1761 1766
1762 1767 listkeys
1763 1768 --------
1764 1769
1765 1770 List values in a specified ``pushkey`` namespace.
1766 1771
1767 1772 The command receives the following arguments:
1768 1773
1769 1774 namespace
1770 1775 (bytestring) Pushkey namespace to query.
1771 1776
1772 1777 The response is a map with bytestring keys and values.
1773 1778
1774 1779 TODO consider using binary to represent nodes in certain pushkey namespaces.
@@ -1,1350 +1,1377 b''
1 1 # wireproto.py - generic wire protocol support functions
2 2 #
3 3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import hashlib
11 11 import os
12 12 import tempfile
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 bin,
17 17 hex,
18 18 nullid,
19 19 )
20 20
21 21 from . import (
22 22 bundle2,
23 23 changegroup as changegroupmod,
24 24 discovery,
25 25 encoding,
26 26 error,
27 27 exchange,
28 28 peer,
29 29 pushkey as pushkeymod,
30 30 pycompat,
31 31 repository,
32 32 streamclone,
33 33 util,
34 34 wireprototypes,
35 35 )
36 36
37 37 from .utils import (
38 38 procutil,
39 39 stringutil,
40 40 )
41 41
42 42 urlerr = util.urlerr
43 43 urlreq = util.urlreq
44 44
45 45 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required')
46 46 bundle2requiredhint = _('see https://www.mercurial-scm.org/wiki/'
47 47 'IncompatibleClient')
48 48 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
49 49
50 50 class remoteiterbatcher(peer.iterbatcher):
51 51 def __init__(self, remote):
52 52 super(remoteiterbatcher, self).__init__()
53 53 self._remote = remote
54 54
55 55 def __getattr__(self, name):
56 56 # Validate this method is batchable, since submit() only supports
57 57 # batchable methods.
58 58 fn = getattr(self._remote, name)
59 59 if not getattr(fn, 'batchable', None):
60 60 raise error.ProgrammingError('Attempted to batch a non-batchable '
61 61 'call to %r' % name)
62 62
63 63 return super(remoteiterbatcher, self).__getattr__(name)
64 64
65 65 def submit(self):
66 66 """Break the batch request into many patch calls and pipeline them.
67 67
68 68 This is mostly valuable over http where request sizes can be
69 69 limited, but can be used in other places as well.
70 70 """
71 71 # 2-tuple of (command, arguments) that represents what will be
72 72 # sent over the wire.
73 73 requests = []
74 74
75 75 # 4-tuple of (command, final future, @batchable generator, remote
76 76 # future).
77 77 results = []
78 78
79 79 for command, args, opts, finalfuture in self.calls:
80 80 mtd = getattr(self._remote, command)
81 81 batchable = mtd.batchable(mtd.__self__, *args, **opts)
82 82
83 83 commandargs, fremote = next(batchable)
84 84 assert fremote
85 85 requests.append((command, commandargs))
86 86 results.append((command, finalfuture, batchable, fremote))
87 87
88 88 if requests:
89 89 self._resultiter = self._remote._submitbatch(requests)
90 90
91 91 self._results = results
92 92
93 93 def results(self):
94 94 for command, finalfuture, batchable, remotefuture in self._results:
95 95 # Get the raw result, set it in the remote future, feed it
96 96 # back into the @batchable generator so it can be decoded, and
97 97 # set the result on the final future to this value.
98 98 remoteresult = next(self._resultiter)
99 99 remotefuture.set(remoteresult)
100 100 finalfuture.set(next(batchable))
101 101
102 102 # Verify our @batchable generators only emit 2 values.
103 103 try:
104 104 next(batchable)
105 105 except StopIteration:
106 106 pass
107 107 else:
108 108 raise error.ProgrammingError('%s @batchable generator emitted '
109 109 'unexpected value count' % command)
110 110
111 111 yield finalfuture.value
112 112
113 113 # Forward a couple of names from peer to make wireproto interactions
114 114 # slightly more sensible.
115 115 batchable = peer.batchable
116 116 future = peer.future
117 117
118 118 # list of nodes encoding / decoding
119 119
120 120 def decodelist(l, sep=' '):
121 121 if l:
122 122 return [bin(v) for v in l.split(sep)]
123 123 return []
124 124
125 125 def encodelist(l, sep=' '):
126 126 try:
127 127 return sep.join(map(hex, l))
128 128 except TypeError:
129 129 raise
130 130
131 131 # batched call argument encoding
132 132
133 133 def escapearg(plain):
134 134 return (plain
135 135 .replace(':', ':c')
136 136 .replace(',', ':o')
137 137 .replace(';', ':s')
138 138 .replace('=', ':e'))
139 139
140 140 def unescapearg(escaped):
141 141 return (escaped
142 142 .replace(':e', '=')
143 143 .replace(':s', ';')
144 144 .replace(':o', ',')
145 145 .replace(':c', ':'))
146 146
147 147 def encodebatchcmds(req):
148 148 """Return a ``cmds`` argument value for the ``batch`` command."""
149 149 cmds = []
150 150 for op, argsdict in req:
151 151 # Old servers didn't properly unescape argument names. So prevent
152 152 # the sending of argument names that may not be decoded properly by
153 153 # servers.
154 154 assert all(escapearg(k) == k for k in argsdict)
155 155
156 156 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
157 157 for k, v in argsdict.iteritems())
158 158 cmds.append('%s %s' % (op, args))
159 159
160 160 return ';'.join(cmds)
161 161
162 162 def clientcompressionsupport(proto):
163 163 """Returns a list of compression methods supported by the client.
164 164
165 165 Returns a list of the compression methods supported by the client
166 166 according to the protocol capabilities. If no such capability has
167 167 been announced, fallback to the default of zlib and uncompressed.
168 168 """
169 169 for cap in proto.getprotocaps():
170 170 if cap.startswith('comp='):
171 171 return cap[5:].split(',')
172 172 return ['zlib', 'none']
173 173
174 174 # mapping of options accepted by getbundle and their types
175 175 #
176 176 # Meant to be extended by extensions. It is extensions responsibility to ensure
177 177 # such options are properly processed in exchange.getbundle.
178 178 #
179 179 # supported types are:
180 180 #
181 181 # :nodes: list of binary nodes
182 182 # :csv: list of comma-separated values
183 183 # :scsv: list of comma-separated values return as set
184 184 # :plain: string with no transformation needed.
185 185 gboptsmap = {'heads': 'nodes',
186 186 'bookmarks': 'boolean',
187 187 'common': 'nodes',
188 188 'obsmarkers': 'boolean',
189 189 'phases': 'boolean',
190 190 'bundlecaps': 'scsv',
191 191 'listkeys': 'csv',
192 192 'cg': 'boolean',
193 193 'cbattempted': 'boolean',
194 194 'stream': 'boolean',
195 195 }
196 196
197 197 # client side
198 198
199 199 class wirepeer(repository.legacypeer):
200 200 """Client-side interface for communicating with a peer repository.
201 201
202 202 Methods commonly call wire protocol commands of the same name.
203 203
204 204 See also httppeer.py and sshpeer.py for protocol-specific
205 205 implementations of this interface.
206 206 """
207 207 # Begin of ipeercommands interface.
208 208
209 209 def iterbatch(self):
210 210 return remoteiterbatcher(self)
211 211
212 212 @batchable
213 213 def lookup(self, key):
214 214 self.requirecap('lookup', _('look up remote revision'))
215 215 f = future()
216 216 yield {'key': encoding.fromlocal(key)}, f
217 217 d = f.value
218 218 success, data = d[:-1].split(" ", 1)
219 219 if int(success):
220 220 yield bin(data)
221 221 else:
222 222 self._abort(error.RepoError(data))
223 223
224 224 @batchable
225 225 def heads(self):
226 226 f = future()
227 227 yield {}, f
228 228 d = f.value
229 229 try:
230 230 yield decodelist(d[:-1])
231 231 except ValueError:
232 232 self._abort(error.ResponseError(_("unexpected response:"), d))
233 233
234 234 @batchable
235 235 def known(self, nodes):
236 236 f = future()
237 237 yield {'nodes': encodelist(nodes)}, f
238 238 d = f.value
239 239 try:
240 240 yield [bool(int(b)) for b in d]
241 241 except ValueError:
242 242 self._abort(error.ResponseError(_("unexpected response:"), d))
243 243
244 244 @batchable
245 245 def branchmap(self):
246 246 f = future()
247 247 yield {}, f
248 248 d = f.value
249 249 try:
250 250 branchmap = {}
251 251 for branchpart in d.splitlines():
252 252 branchname, branchheads = branchpart.split(' ', 1)
253 253 branchname = encoding.tolocal(urlreq.unquote(branchname))
254 254 branchheads = decodelist(branchheads)
255 255 branchmap[branchname] = branchheads
256 256 yield branchmap
257 257 except TypeError:
258 258 self._abort(error.ResponseError(_("unexpected response:"), d))
259 259
260 260 @batchable
261 261 def listkeys(self, namespace):
262 262 if not self.capable('pushkey'):
263 263 yield {}, None
264 264 f = future()
265 265 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
266 266 yield {'namespace': encoding.fromlocal(namespace)}, f
267 267 d = f.value
268 268 self.ui.debug('received listkey for "%s": %i bytes\n'
269 269 % (namespace, len(d)))
270 270 yield pushkeymod.decodekeys(d)
271 271
272 272 @batchable
273 273 def pushkey(self, namespace, key, old, new):
274 274 if not self.capable('pushkey'):
275 275 yield False, None
276 276 f = future()
277 277 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
278 278 yield {'namespace': encoding.fromlocal(namespace),
279 279 'key': encoding.fromlocal(key),
280 280 'old': encoding.fromlocal(old),
281 281 'new': encoding.fromlocal(new)}, f
282 282 d = f.value
283 283 d, output = d.split('\n', 1)
284 284 try:
285 285 d = bool(int(d))
286 286 except ValueError:
287 287 raise error.ResponseError(
288 288 _('push failed (unexpected response):'), d)
289 289 for l in output.splitlines(True):
290 290 self.ui.status(_('remote: '), l)
291 291 yield d
292 292
293 293 def stream_out(self):
294 294 return self._callstream('stream_out')
295 295
296 296 def getbundle(self, source, **kwargs):
297 297 kwargs = pycompat.byteskwargs(kwargs)
298 298 self.requirecap('getbundle', _('look up remote changes'))
299 299 opts = {}
300 300 bundlecaps = kwargs.get('bundlecaps') or set()
301 301 for key, value in kwargs.iteritems():
302 302 if value is None:
303 303 continue
304 304 keytype = gboptsmap.get(key)
305 305 if keytype is None:
306 306 raise error.ProgrammingError(
307 307 'Unexpectedly None keytype for key %s' % key)
308 308 elif keytype == 'nodes':
309 309 value = encodelist(value)
310 310 elif keytype == 'csv':
311 311 value = ','.join(value)
312 312 elif keytype == 'scsv':
313 313 value = ','.join(sorted(value))
314 314 elif keytype == 'boolean':
315 315 value = '%i' % bool(value)
316 316 elif keytype != 'plain':
317 317 raise KeyError('unknown getbundle option type %s'
318 318 % keytype)
319 319 opts[key] = value
320 320 f = self._callcompressable("getbundle", **pycompat.strkwargs(opts))
321 321 if any((cap.startswith('HG2') for cap in bundlecaps)):
322 322 return bundle2.getunbundler(self.ui, f)
323 323 else:
324 324 return changegroupmod.cg1unpacker(f, 'UN')
325 325
326 326 def unbundle(self, cg, heads, url):
327 327 '''Send cg (a readable file-like object representing the
328 328 changegroup to push, typically a chunkbuffer object) to the
329 329 remote server as a bundle.
330 330
331 331 When pushing a bundle10 stream, return an integer indicating the
332 332 result of the push (see changegroup.apply()).
333 333
334 334 When pushing a bundle20 stream, return a bundle20 stream.
335 335
336 336 `url` is the url the client thinks it's pushing to, which is
337 337 visible to hooks.
338 338 '''
339 339
340 340 if heads != ['force'] and self.capable('unbundlehash'):
341 341 heads = encodelist(['hashed',
342 342 hashlib.sha1(''.join(sorted(heads))).digest()])
343 343 else:
344 344 heads = encodelist(heads)
345 345
346 346 if util.safehasattr(cg, 'deltaheader'):
347 347 # this a bundle10, do the old style call sequence
348 348 ret, output = self._callpush("unbundle", cg, heads=heads)
349 349 if ret == "":
350 350 raise error.ResponseError(
351 351 _('push failed:'), output)
352 352 try:
353 353 ret = int(ret)
354 354 except ValueError:
355 355 raise error.ResponseError(
356 356 _('push failed (unexpected response):'), ret)
357 357
358 358 for l in output.splitlines(True):
359 359 self.ui.status(_('remote: '), l)
360 360 else:
361 361 # bundle2 push. Send a stream, fetch a stream.
362 362 stream = self._calltwowaystream('unbundle', cg, heads=heads)
363 363 ret = bundle2.getunbundler(self.ui, stream)
364 364 return ret
365 365
366 366 # End of ipeercommands interface.
367 367
368 368 # Begin of ipeerlegacycommands interface.
369 369
370 370 def branches(self, nodes):
371 371 n = encodelist(nodes)
372 372 d = self._call("branches", nodes=n)
373 373 try:
374 374 br = [tuple(decodelist(b)) for b in d.splitlines()]
375 375 return br
376 376 except ValueError:
377 377 self._abort(error.ResponseError(_("unexpected response:"), d))
378 378
379 379 def between(self, pairs):
380 380 batch = 8 # avoid giant requests
381 381 r = []
382 382 for i in xrange(0, len(pairs), batch):
383 383 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
384 384 d = self._call("between", pairs=n)
385 385 try:
386 386 r.extend(l and decodelist(l) or [] for l in d.splitlines())
387 387 except ValueError:
388 388 self._abort(error.ResponseError(_("unexpected response:"), d))
389 389 return r
390 390
391 391 def changegroup(self, nodes, kind):
392 392 n = encodelist(nodes)
393 393 f = self._callcompressable("changegroup", roots=n)
394 394 return changegroupmod.cg1unpacker(f, 'UN')
395 395
396 396 def changegroupsubset(self, bases, heads, kind):
397 397 self.requirecap('changegroupsubset', _('look up remote changes'))
398 398 bases = encodelist(bases)
399 399 heads = encodelist(heads)
400 400 f = self._callcompressable("changegroupsubset",
401 401 bases=bases, heads=heads)
402 402 return changegroupmod.cg1unpacker(f, 'UN')
403 403
404 404 # End of ipeerlegacycommands interface.
405 405
406 406 def _submitbatch(self, req):
407 407 """run batch request <req> on the server
408 408
409 409 Returns an iterator of the raw responses from the server.
410 410 """
411 411 ui = self.ui
412 412 if ui.debugflag and ui.configbool('devel', 'debug.peer-request'):
413 413 ui.debug('devel-peer-request: batched-content\n')
414 414 for op, args in req:
415 415 msg = 'devel-peer-request: - %s (%d arguments)\n'
416 416 ui.debug(msg % (op, len(args)))
417 417
418 418 rsp = self._callstream("batch", cmds=encodebatchcmds(req))
419 419 chunk = rsp.read(1024)
420 420 work = [chunk]
421 421 while chunk:
422 422 while ';' not in chunk and chunk:
423 423 chunk = rsp.read(1024)
424 424 work.append(chunk)
425 425 merged = ''.join(work)
426 426 while ';' in merged:
427 427 one, merged = merged.split(';', 1)
428 428 yield unescapearg(one)
429 429 chunk = rsp.read(1024)
430 430 work = [merged, chunk]
431 431 yield unescapearg(''.join(work))
432 432
433 433 def _submitone(self, op, args):
434 434 return self._call(op, **pycompat.strkwargs(args))
435 435
436 436 def debugwireargs(self, one, two, three=None, four=None, five=None):
437 437 # don't pass optional arguments left at their default value
438 438 opts = {}
439 439 if three is not None:
440 440 opts[r'three'] = three
441 441 if four is not None:
442 442 opts[r'four'] = four
443 443 return self._call('debugwireargs', one=one, two=two, **opts)
444 444
445 445 def _call(self, cmd, **args):
446 446 """execute <cmd> on the server
447 447
448 448 The command is expected to return a simple string.
449 449
450 450 returns the server reply as a string."""
451 451 raise NotImplementedError()
452 452
453 453 def _callstream(self, cmd, **args):
454 454 """execute <cmd> on the server
455 455
456 456 The command is expected to return a stream. Note that if the
457 457 command doesn't return a stream, _callstream behaves
458 458 differently for ssh and http peers.
459 459
460 460 returns the server reply as a file like object.
461 461 """
462 462 raise NotImplementedError()
463 463
464 464 def _callcompressable(self, cmd, **args):
465 465 """execute <cmd> on the server
466 466
467 467 The command is expected to return a stream.
468 468
469 469 The stream may have been compressed in some implementations. This
470 470 function takes care of the decompression. This is the only difference
471 471 with _callstream.
472 472
473 473 returns the server reply as a file like object.
474 474 """
475 475 raise NotImplementedError()
476 476
477 477 def _callpush(self, cmd, fp, **args):
478 478 """execute a <cmd> on server
479 479
480 480 The command is expected to be related to a push. Push has a special
481 481 return method.
482 482
483 483 returns the server reply as a (ret, output) tuple. ret is either
484 484 empty (error) or a stringified int.
485 485 """
486 486 raise NotImplementedError()
487 487
488 488 def _calltwowaystream(self, cmd, fp, **args):
489 489 """execute <cmd> on server
490 490
491 491 The command will send a stream to the server and get a stream in reply.
492 492 """
493 493 raise NotImplementedError()
494 494
495 495 def _abort(self, exception):
496 496 """clearly abort the wire protocol connection and raise the exception
497 497 """
498 498 raise NotImplementedError()
499 499
500 500 # server side
501 501
502 502 # wire protocol command can either return a string or one of these classes.
503 503
504 504 def getdispatchrepo(repo, proto, command):
505 505 """Obtain the repo used for processing wire protocol commands.
506 506
507 507 The intent of this function is to serve as a monkeypatch point for
508 508 extensions that need commands to operate on different repo views under
509 509 specialized circumstances.
510 510 """
511 511 return repo.filtered('served')
512 512
513 513 def dispatch(repo, proto, command):
514 514 repo = getdispatchrepo(repo, proto, command)
515 515
516 516 transportversion = wireprototypes.TRANSPORTS[proto.name]['version']
517 517 commandtable = commandsv2 if transportversion == 2 else commands
518 518 func, spec = commandtable[command]
519 519
520 520 args = proto.getargs(spec)
521 521
522 522 # Version 1 protocols define arguments as a list. Version 2 uses a dict.
523 523 if isinstance(args, list):
524 524 return func(repo, proto, *args)
525 525 elif isinstance(args, dict):
526 526 return func(repo, proto, **args)
527 527 else:
528 528 raise error.ProgrammingError('unexpected type returned from '
529 529 'proto.getargs(): %s' % type(args))
530 530
531 531 def options(cmd, keys, others):
532 532 opts = {}
533 533 for k in keys:
534 534 if k in others:
535 535 opts[k] = others[k]
536 536 del others[k]
537 537 if others:
538 538 procutil.stderr.write("warning: %s ignored unexpected arguments %s\n"
539 539 % (cmd, ",".join(others)))
540 540 return opts
541 541
542 542 def bundle1allowed(repo, action):
543 543 """Whether a bundle1 operation is allowed from the server.
544 544
545 545 Priority is:
546 546
547 547 1. server.bundle1gd.<action> (if generaldelta active)
548 548 2. server.bundle1.<action>
549 549 3. server.bundle1gd (if generaldelta active)
550 550 4. server.bundle1
551 551 """
552 552 ui = repo.ui
553 553 gd = 'generaldelta' in repo.requirements
554 554
555 555 if gd:
556 556 v = ui.configbool('server', 'bundle1gd.%s' % action)
557 557 if v is not None:
558 558 return v
559 559
560 560 v = ui.configbool('server', 'bundle1.%s' % action)
561 561 if v is not None:
562 562 return v
563 563
564 564 if gd:
565 565 v = ui.configbool('server', 'bundle1gd')
566 566 if v is not None:
567 567 return v
568 568
569 569 return ui.configbool('server', 'bundle1')
570 570
571 571 def supportedcompengines(ui, role):
572 572 """Obtain the list of supported compression engines for a request."""
573 573 assert role in (util.CLIENTROLE, util.SERVERROLE)
574 574
575 575 compengines = util.compengines.supportedwireengines(role)
576 576
577 577 # Allow config to override default list and ordering.
578 578 if role == util.SERVERROLE:
579 579 configengines = ui.configlist('server', 'compressionengines')
580 580 config = 'server.compressionengines'
581 581 else:
582 582 # This is currently implemented mainly to facilitate testing. In most
583 583 # cases, the server should be in charge of choosing a compression engine
584 584 # because a server has the most to lose from a sub-optimal choice. (e.g.
585 585 # CPU DoS due to an expensive engine or a network DoS due to poor
586 586 # compression ratio).
587 587 configengines = ui.configlist('experimental',
588 588 'clientcompressionengines')
589 589 config = 'experimental.clientcompressionengines'
590 590
591 591 # No explicit config. Filter out the ones that aren't supposed to be
592 592 # advertised and return default ordering.
593 593 if not configengines:
594 594 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
595 595 return [e for e in compengines
596 596 if getattr(e.wireprotosupport(), attr) > 0]
597 597
598 598 # If compression engines are listed in the config, assume there is a good
599 599 # reason for it (like server operators wanting to achieve specific
600 600 # performance characteristics). So fail fast if the config references
601 601 # unusable compression engines.
602 602 validnames = set(e.name() for e in compengines)
603 603 invalidnames = set(e for e in configengines if e not in validnames)
604 604 if invalidnames:
605 605 raise error.Abort(_('invalid compression engine defined in %s: %s') %
606 606 (config, ', '.join(sorted(invalidnames))))
607 607
608 608 compengines = [e for e in compengines if e.name() in configengines]
609 609 compengines = sorted(compengines,
610 610 key=lambda e: configengines.index(e.name()))
611 611
612 612 if not compengines:
613 613 raise error.Abort(_('%s config option does not specify any known '
614 614 'compression engines') % config,
615 615 hint=_('usable compression engines: %s') %
616 616 ', '.sorted(validnames))
617 617
618 618 return compengines
619 619
620 620 class commandentry(object):
621 621 """Represents a declared wire protocol command."""
622 622 def __init__(self, func, args='', transports=None,
623 623 permission='push'):
624 624 self.func = func
625 625 self.args = args
626 626 self.transports = transports or set()
627 627 self.permission = permission
628 628
629 629 def _merge(self, func, args):
630 630 """Merge this instance with an incoming 2-tuple.
631 631
632 632 This is called when a caller using the old 2-tuple API attempts
633 633 to replace an instance. The incoming values are merged with
634 634 data not captured by the 2-tuple and a new instance containing
635 635 the union of the two objects is returned.
636 636 """
637 637 return commandentry(func, args=args, transports=set(self.transports),
638 638 permission=self.permission)
639 639
640 640 # Old code treats instances as 2-tuples. So expose that interface.
641 641 def __iter__(self):
642 642 yield self.func
643 643 yield self.args
644 644
645 645 def __getitem__(self, i):
646 646 if i == 0:
647 647 return self.func
648 648 elif i == 1:
649 649 return self.args
650 650 else:
651 651 raise IndexError('can only access elements 0 and 1')
652 652
653 653 class commanddict(dict):
654 654 """Container for registered wire protocol commands.
655 655
656 656 It behaves like a dict. But __setitem__ is overwritten to allow silent
657 657 coercion of values from 2-tuples for API compatibility.
658 658 """
659 659 def __setitem__(self, k, v):
660 660 if isinstance(v, commandentry):
661 661 pass
662 662 # Cast 2-tuples to commandentry instances.
663 663 elif isinstance(v, tuple):
664 664 if len(v) != 2:
665 665 raise ValueError('command tuples must have exactly 2 elements')
666 666
667 667 # It is common for extensions to wrap wire protocol commands via
668 668 # e.g. ``wireproto.commands[x] = (newfn, args)``. Because callers
669 669 # doing this aren't aware of the new API that uses objects to store
670 670 # command entries, we automatically merge old state with new.
671 671 if k in self:
672 672 v = self[k]._merge(v[0], v[1])
673 673 else:
674 674 # Use default values from @wireprotocommand.
675 675 v = commandentry(v[0], args=v[1],
676 676 transports=set(wireprototypes.TRANSPORTS),
677 677 permission='push')
678 678 else:
679 679 raise ValueError('command entries must be commandentry instances '
680 680 'or 2-tuples')
681 681
682 682 return super(commanddict, self).__setitem__(k, v)
683 683
684 684 def commandavailable(self, command, proto):
685 685 """Determine if a command is available for the requested protocol."""
686 686 assert proto.name in wireprototypes.TRANSPORTS
687 687
688 688 entry = self.get(command)
689 689
690 690 if not entry:
691 691 return False
692 692
693 693 if proto.name not in entry.transports:
694 694 return False
695 695
696 696 return True
697 697
698 698 # Constants specifying which transports a wire protocol command should be
699 699 # available on. For use with @wireprotocommand.
700 700 POLICY_ALL = 'all'
701 701 POLICY_V1_ONLY = 'v1-only'
702 702 POLICY_V2_ONLY = 'v2-only'
703 703
704 704 # For version 1 transports.
705 705 commands = commanddict()
706 706
707 707 # For version 2 transports.
708 708 commandsv2 = commanddict()
709 709
710 710 def wireprotocommand(name, args='', transportpolicy=POLICY_ALL,
711 711 permission='push'):
712 712 """Decorator to declare a wire protocol command.
713 713
714 714 ``name`` is the name of the wire protocol command being provided.
715 715
716 ``args`` is a space-delimited list of named arguments that the command
717 accepts. ``*`` is a special value that says to accept all arguments.
716 ``args`` defines the named arguments accepted by the command. It is
717 ideally a dict mapping argument names to their types. For backwards
718 compatibility, it can be a space-delimited list of argument names. For
719 version 1 transports, ``*`` denotes a special value that says to accept
720 all named arguments.
718 721
719 722 ``transportpolicy`` is a POLICY_* constant denoting which transports
720 723 this wire protocol command should be exposed to. By default, commands
721 724 are exposed to all wire protocol transports.
722 725
723 726 ``permission`` defines the permission type needed to run this command.
724 727 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
725 728 respectively. Default is to assume command requires ``push`` permissions
726 729 because otherwise commands not declaring their permissions could modify
727 730 a repository that is supposed to be read-only.
728 731 """
729 732 if transportpolicy == POLICY_ALL:
730 733 transports = set(wireprototypes.TRANSPORTS)
731 734 transportversions = {1, 2}
732 735 elif transportpolicy == POLICY_V1_ONLY:
733 736 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
734 737 if v['version'] == 1}
735 738 transportversions = {1}
736 739 elif transportpolicy == POLICY_V2_ONLY:
737 740 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
738 741 if v['version'] == 2}
739 742 transportversions = {2}
740 743 else:
741 744 raise error.ProgrammingError('invalid transport policy value: %s' %
742 745 transportpolicy)
743 746
744 747 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to
745 748 # SSHv2.
746 749 # TODO undo this hack when SSH is using the unified frame protocol.
747 750 if name == b'batch':
748 751 transports.add(wireprototypes.SSHV2)
749 752
750 753 if permission not in ('push', 'pull'):
751 754 raise error.ProgrammingError('invalid wire protocol permission; '
752 755 'got %s; expected "push" or "pull"' %
753 756 permission)
754 757
758 if 1 in transportversions and not isinstance(args, bytes):
759 raise error.ProgrammingError('arguments for version 1 commands must '
760 'be declared as bytes')
761
762 if isinstance(args, bytes):
763 dictargs = {arg: b'legacy' for arg in args.split()}
764 elif isinstance(args, dict):
765 dictargs = args
766 else:
767 raise ValueError('args must be bytes or a dict')
768
755 769 def register(func):
756 770 if 1 in transportversions:
757 771 if name in commands:
758 772 raise error.ProgrammingError('%s command already registered '
759 773 'for version 1' % name)
760 774 commands[name] = commandentry(func, args=args,
761 775 transports=transports,
762 776 permission=permission)
763 777 if 2 in transportversions:
764 778 if name in commandsv2:
765 779 raise error.ProgrammingError('%s command already registered '
766 780 'for version 2' % name)
767 commandsv2[name] = commandentry(func, args=args,
781
782 commandsv2[name] = commandentry(func, args=dictargs,
768 783 transports=transports,
769 784 permission=permission)
770 785
771 786 return func
772 787 return register
773 788
774 789 # TODO define a more appropriate permissions type to use for this.
775 790 @wireprotocommand('batch', 'cmds *', permission='pull',
776 791 transportpolicy=POLICY_V1_ONLY)
777 792 def batch(repo, proto, cmds, others):
778 793 repo = repo.filtered("served")
779 794 res = []
780 795 for pair in cmds.split(';'):
781 796 op, args = pair.split(' ', 1)
782 797 vals = {}
783 798 for a in args.split(','):
784 799 if a:
785 800 n, v = a.split('=')
786 801 vals[unescapearg(n)] = unescapearg(v)
787 802 func, spec = commands[op]
788 803
789 804 # Validate that client has permissions to perform this command.
790 805 perm = commands[op].permission
791 806 assert perm in ('push', 'pull')
792 807 proto.checkperm(perm)
793 808
794 809 if spec:
795 810 keys = spec.split()
796 811 data = {}
797 812 for k in keys:
798 813 if k == '*':
799 814 star = {}
800 815 for key in vals.keys():
801 816 if key not in keys:
802 817 star[key] = vals[key]
803 818 data['*'] = star
804 819 else:
805 820 data[k] = vals[k]
806 821 result = func(repo, proto, *[data[k] for k in keys])
807 822 else:
808 823 result = func(repo, proto)
809 824 if isinstance(result, wireprototypes.ooberror):
810 825 return result
811 826
812 827 # For now, all batchable commands must return bytesresponse or
813 828 # raw bytes (for backwards compatibility).
814 829 assert isinstance(result, (wireprototypes.bytesresponse, bytes))
815 830 if isinstance(result, wireprototypes.bytesresponse):
816 831 result = result.data
817 832 res.append(escapearg(result))
818 833
819 834 return wireprototypes.bytesresponse(';'.join(res))
820 835
821 836 @wireprotocommand('between', 'pairs', transportpolicy=POLICY_V1_ONLY,
822 837 permission='pull')
823 838 def between(repo, proto, pairs):
824 839 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
825 840 r = []
826 841 for b in repo.between(pairs):
827 842 r.append(encodelist(b) + "\n")
828 843
829 844 return wireprototypes.bytesresponse(''.join(r))
830 845
831 846 @wireprotocommand('branchmap', permission='pull',
832 847 transportpolicy=POLICY_V1_ONLY)
833 848 def branchmap(repo, proto):
834 849 branchmap = repo.branchmap()
835 850 heads = []
836 851 for branch, nodes in branchmap.iteritems():
837 852 branchname = urlreq.quote(encoding.fromlocal(branch))
838 853 branchnodes = encodelist(nodes)
839 854 heads.append('%s %s' % (branchname, branchnodes))
840 855
841 856 return wireprototypes.bytesresponse('\n'.join(heads))
842 857
843 858 @wireprotocommand('branches', 'nodes', transportpolicy=POLICY_V1_ONLY,
844 859 permission='pull')
845 860 def branches(repo, proto, nodes):
846 861 nodes = decodelist(nodes)
847 862 r = []
848 863 for b in repo.branches(nodes):
849 864 r.append(encodelist(b) + "\n")
850 865
851 866 return wireprototypes.bytesresponse(''.join(r))
852 867
853 868 @wireprotocommand('clonebundles', '', permission='pull')
854 869 def clonebundles(repo, proto):
855 870 """Server command for returning info for available bundles to seed clones.
856 871
857 872 Clients will parse this response and determine what bundle to fetch.
858 873
859 874 Extensions may wrap this command to filter or dynamically emit data
860 875 depending on the request. e.g. you could advertise URLs for the closest
861 876 data center given the client's IP address.
862 877 """
863 878 return wireprototypes.bytesresponse(
864 879 repo.vfs.tryread('clonebundles.manifest'))
865 880
866 881 wireprotocaps = ['lookup', 'branchmap', 'pushkey',
867 882 'known', 'getbundle', 'unbundlehash']
868 883
869 884 def _capabilities(repo, proto):
870 885 """return a list of capabilities for a repo
871 886
872 887 This function exists to allow extensions to easily wrap capabilities
873 888 computation
874 889
875 890 - returns a lists: easy to alter
876 891 - change done here will be propagated to both `capabilities` and `hello`
877 892 command without any other action needed.
878 893 """
879 894 # copy to prevent modification of the global list
880 895 caps = list(wireprotocaps)
881 896
882 897 # Command of same name as capability isn't exposed to version 1 of
883 898 # transports. So conditionally add it.
884 899 if commands.commandavailable('changegroupsubset', proto):
885 900 caps.append('changegroupsubset')
886 901
887 902 if streamclone.allowservergeneration(repo):
888 903 if repo.ui.configbool('server', 'preferuncompressed'):
889 904 caps.append('stream-preferred')
890 905 requiredformats = repo.requirements & repo.supportedformats
891 906 # if our local revlogs are just revlogv1, add 'stream' cap
892 907 if not requiredformats - {'revlogv1'}:
893 908 caps.append('stream')
894 909 # otherwise, add 'streamreqs' detailing our local revlog format
895 910 else:
896 911 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
897 912 if repo.ui.configbool('experimental', 'bundle2-advertise'):
898 913 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role='server'))
899 914 caps.append('bundle2=' + urlreq.quote(capsblob))
900 915 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
901 916
902 917 return proto.addcapabilities(repo, caps)
903 918
904 919 # If you are writing an extension and consider wrapping this function. Wrap
905 920 # `_capabilities` instead.
906 921 @wireprotocommand('capabilities', permission='pull',
907 922 transportpolicy=POLICY_V1_ONLY)
908 923 def capabilities(repo, proto):
909 924 caps = _capabilities(repo, proto)
910 925 return wireprototypes.bytesresponse(' '.join(sorted(caps)))
911 926
912 927 @wireprotocommand('changegroup', 'roots', transportpolicy=POLICY_V1_ONLY,
913 928 permission='pull')
914 929 def changegroup(repo, proto, roots):
915 930 nodes = decodelist(roots)
916 931 outgoing = discovery.outgoing(repo, missingroots=nodes,
917 932 missingheads=repo.heads())
918 933 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
919 934 gen = iter(lambda: cg.read(32768), '')
920 935 return wireprototypes.streamres(gen=gen)
921 936
922 937 @wireprotocommand('changegroupsubset', 'bases heads',
923 938 transportpolicy=POLICY_V1_ONLY,
924 939 permission='pull')
925 940 def changegroupsubset(repo, proto, bases, heads):
926 941 bases = decodelist(bases)
927 942 heads = decodelist(heads)
928 943 outgoing = discovery.outgoing(repo, missingroots=bases,
929 944 missingheads=heads)
930 945 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
931 946 gen = iter(lambda: cg.read(32768), '')
932 947 return wireprototypes.streamres(gen=gen)
933 948
934 949 @wireprotocommand('debugwireargs', 'one two *',
935 950 permission='pull', transportpolicy=POLICY_V1_ONLY)
936 951 def debugwireargs(repo, proto, one, two, others):
937 952 # only accept optional args from the known set
938 953 opts = options('debugwireargs', ['three', 'four'], others)
939 954 return wireprototypes.bytesresponse(repo.debugwireargs(
940 955 one, two, **pycompat.strkwargs(opts)))
941 956
942 957 def find_pullbundle(repo, proto, opts, clheads, heads, common):
943 958 """Return a file object for the first matching pullbundle.
944 959
945 960 Pullbundles are specified in .hg/pullbundles.manifest similar to
946 961 clonebundles.
947 962 For each entry, the bundle specification is checked for compatibility:
948 963 - Client features vs the BUNDLESPEC.
949 964 - Revisions shared with the clients vs base revisions of the bundle.
950 965 A bundle can be applied only if all its base revisions are known by
951 966 the client.
952 967 - At least one leaf of the bundle's DAG is missing on the client.
953 968 - Every leaf of the bundle's DAG is part of node set the client wants.
954 969 E.g. do not send a bundle of all changes if the client wants only
955 970 one specific branch of many.
956 971 """
957 972 def decodehexstring(s):
958 973 return set([h.decode('hex') for h in s.split(';')])
959 974
960 975 manifest = repo.vfs.tryread('pullbundles.manifest')
961 976 if not manifest:
962 977 return None
963 978 res = exchange.parseclonebundlesmanifest(repo, manifest)
964 979 res = exchange.filterclonebundleentries(repo, res)
965 980 if not res:
966 981 return None
967 982 cl = repo.changelog
968 983 heads_anc = cl.ancestors([cl.rev(rev) for rev in heads], inclusive=True)
969 984 common_anc = cl.ancestors([cl.rev(rev) for rev in common], inclusive=True)
970 985 compformats = clientcompressionsupport(proto)
971 986 for entry in res:
972 987 if 'COMPRESSION' in entry and entry['COMPRESSION'] not in compformats:
973 988 continue
974 989 # No test yet for VERSION, since V2 is supported by any client
975 990 # that advertises partial pulls
976 991 if 'heads' in entry:
977 992 try:
978 993 bundle_heads = decodehexstring(entry['heads'])
979 994 except TypeError:
980 995 # Bad heads entry
981 996 continue
982 997 if bundle_heads.issubset(common):
983 998 continue # Nothing new
984 999 if all(cl.rev(rev) in common_anc for rev in bundle_heads):
985 1000 continue # Still nothing new
986 1001 if any(cl.rev(rev) not in heads_anc and
987 1002 cl.rev(rev) not in common_anc for rev in bundle_heads):
988 1003 continue
989 1004 if 'bases' in entry:
990 1005 try:
991 1006 bundle_bases = decodehexstring(entry['bases'])
992 1007 except TypeError:
993 1008 # Bad bases entry
994 1009 continue
995 1010 if not all(cl.rev(rev) in common_anc for rev in bundle_bases):
996 1011 continue
997 1012 path = entry['URL']
998 1013 repo.ui.debug('sending pullbundle "%s"\n' % path)
999 1014 try:
1000 1015 return repo.vfs.open(path)
1001 1016 except IOError:
1002 1017 repo.ui.debug('pullbundle "%s" not accessible\n' % path)
1003 1018 continue
1004 1019 return None
1005 1020
1006 1021 @wireprotocommand('getbundle', '*', permission='pull')
1007 1022 def getbundle(repo, proto, others):
1008 1023 opts = options('getbundle', gboptsmap.keys(), others)
1009 1024 for k, v in opts.iteritems():
1010 1025 keytype = gboptsmap[k]
1011 1026 if keytype == 'nodes':
1012 1027 opts[k] = decodelist(v)
1013 1028 elif keytype == 'csv':
1014 1029 opts[k] = list(v.split(','))
1015 1030 elif keytype == 'scsv':
1016 1031 opts[k] = set(v.split(','))
1017 1032 elif keytype == 'boolean':
1018 1033 # Client should serialize False as '0', which is a non-empty string
1019 1034 # so it evaluates as a True bool.
1020 1035 if v == '0':
1021 1036 opts[k] = False
1022 1037 else:
1023 1038 opts[k] = bool(v)
1024 1039 elif keytype != 'plain':
1025 1040 raise KeyError('unknown getbundle option type %s'
1026 1041 % keytype)
1027 1042
1028 1043 if not bundle1allowed(repo, 'pull'):
1029 1044 if not exchange.bundle2requested(opts.get('bundlecaps')):
1030 1045 if proto.name == 'http-v1':
1031 1046 return wireprototypes.ooberror(bundle2required)
1032 1047 raise error.Abort(bundle2requiredmain,
1033 1048 hint=bundle2requiredhint)
1034 1049
1035 1050 prefercompressed = True
1036 1051
1037 1052 try:
1038 1053 clheads = set(repo.changelog.heads())
1039 1054 heads = set(opts.get('heads', set()))
1040 1055 common = set(opts.get('common', set()))
1041 1056 common.discard(nullid)
1042 1057 if (repo.ui.configbool('server', 'pullbundle') and
1043 1058 'partial-pull' in proto.getprotocaps()):
1044 1059 # Check if a pre-built bundle covers this request.
1045 1060 bundle = find_pullbundle(repo, proto, opts, clheads, heads, common)
1046 1061 if bundle:
1047 1062 return wireprototypes.streamres(gen=util.filechunkiter(bundle),
1048 1063 prefer_uncompressed=True)
1049 1064
1050 1065 if repo.ui.configbool('server', 'disablefullbundle'):
1051 1066 # Check to see if this is a full clone.
1052 1067 changegroup = opts.get('cg', True)
1053 1068 if changegroup and not common and clheads == heads:
1054 1069 raise error.Abort(
1055 1070 _('server has pull-based clones disabled'),
1056 1071 hint=_('remove --pull if specified or upgrade Mercurial'))
1057 1072
1058 1073 info, chunks = exchange.getbundlechunks(repo, 'serve',
1059 1074 **pycompat.strkwargs(opts))
1060 1075 prefercompressed = info.get('prefercompressed', True)
1061 1076 except error.Abort as exc:
1062 1077 # cleanly forward Abort error to the client
1063 1078 if not exchange.bundle2requested(opts.get('bundlecaps')):
1064 1079 if proto.name == 'http-v1':
1065 1080 return wireprototypes.ooberror(pycompat.bytestr(exc) + '\n')
1066 1081 raise # cannot do better for bundle1 + ssh
1067 1082 # bundle2 request expect a bundle2 reply
1068 1083 bundler = bundle2.bundle20(repo.ui)
1069 1084 manargs = [('message', pycompat.bytestr(exc))]
1070 1085 advargs = []
1071 1086 if exc.hint is not None:
1072 1087 advargs.append(('hint', exc.hint))
1073 1088 bundler.addpart(bundle2.bundlepart('error:abort',
1074 1089 manargs, advargs))
1075 1090 chunks = bundler.getchunks()
1076 1091 prefercompressed = False
1077 1092
1078 1093 return wireprototypes.streamres(
1079 1094 gen=chunks, prefer_uncompressed=not prefercompressed)
1080 1095
1081 1096 @wireprotocommand('heads', permission='pull', transportpolicy=POLICY_V1_ONLY)
1082 1097 def heads(repo, proto):
1083 1098 h = repo.heads()
1084 1099 return wireprototypes.bytesresponse(encodelist(h) + '\n')
1085 1100
1086 1101 @wireprotocommand('hello', permission='pull', transportpolicy=POLICY_V1_ONLY)
1087 1102 def hello(repo, proto):
1088 1103 """Called as part of SSH handshake to obtain server info.
1089 1104
1090 1105 Returns a list of lines describing interesting things about the
1091 1106 server, in an RFC822-like format.
1092 1107
1093 1108 Currently, the only one defined is ``capabilities``, which consists of a
1094 1109 line of space separated tokens describing server abilities:
1095 1110
1096 1111 capabilities: <token0> <token1> <token2>
1097 1112 """
1098 1113 caps = capabilities(repo, proto).data
1099 1114 return wireprototypes.bytesresponse('capabilities: %s\n' % caps)
1100 1115
1101 1116 @wireprotocommand('listkeys', 'namespace', permission='pull',
1102 1117 transportpolicy=POLICY_V1_ONLY)
1103 1118 def listkeys(repo, proto, namespace):
1104 1119 d = sorted(repo.listkeys(encoding.tolocal(namespace)).items())
1105 1120 return wireprototypes.bytesresponse(pushkeymod.encodekeys(d))
1106 1121
1107 1122 @wireprotocommand('lookup', 'key', permission='pull')
1108 1123 def lookup(repo, proto, key):
1109 1124 try:
1110 1125 k = encoding.tolocal(key)
1111 1126 n = repo.lookup(k)
1112 1127 r = hex(n)
1113 1128 success = 1
1114 1129 except Exception as inst:
1115 1130 r = stringutil.forcebytestr(inst)
1116 1131 success = 0
1117 1132 return wireprototypes.bytesresponse('%d %s\n' % (success, r))
1118 1133
1119 1134 @wireprotocommand('known', 'nodes *', permission='pull',
1120 1135 transportpolicy=POLICY_V1_ONLY)
1121 1136 def known(repo, proto, nodes, others):
1122 1137 v = ''.join(b and '1' or '0' for b in repo.known(decodelist(nodes)))
1123 1138 return wireprototypes.bytesresponse(v)
1124 1139
1125 1140 @wireprotocommand('protocaps', 'caps', permission='pull',
1126 1141 transportpolicy=POLICY_V1_ONLY)
1127 1142 def protocaps(repo, proto, caps):
1128 1143 if proto.name == wireprototypes.SSHV1:
1129 1144 proto._protocaps = set(caps.split(' '))
1130 1145 return wireprototypes.bytesresponse('OK')
1131 1146
1132 1147 @wireprotocommand('pushkey', 'namespace key old new', permission='push')
1133 1148 def pushkey(repo, proto, namespace, key, old, new):
1134 1149 # compatibility with pre-1.8 clients which were accidentally
1135 1150 # sending raw binary nodes rather than utf-8-encoded hex
1136 1151 if len(new) == 20 and stringutil.escapestr(new) != new:
1137 1152 # looks like it could be a binary node
1138 1153 try:
1139 1154 new.decode('utf-8')
1140 1155 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
1141 1156 except UnicodeDecodeError:
1142 1157 pass # binary, leave unmodified
1143 1158 else:
1144 1159 new = encoding.tolocal(new) # normal path
1145 1160
1146 1161 with proto.mayberedirectstdio() as output:
1147 1162 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
1148 1163 encoding.tolocal(old), new) or False
1149 1164
1150 1165 output = output.getvalue() if output else ''
1151 1166 return wireprototypes.bytesresponse('%d\n%s' % (int(r), output))
1152 1167
1153 1168 @wireprotocommand('stream_out', permission='pull',
1154 1169 transportpolicy=POLICY_V1_ONLY)
1155 1170 def stream(repo, proto):
1156 1171 '''If the server supports streaming clone, it advertises the "stream"
1157 1172 capability with a value representing the version and flags of the repo
1158 1173 it is serving. Client checks to see if it understands the format.
1159 1174 '''
1160 1175 return wireprototypes.streamreslegacy(
1161 1176 streamclone.generatev1wireproto(repo))
1162 1177
1163 1178 @wireprotocommand('unbundle', 'heads', permission='push')
1164 1179 def unbundle(repo, proto, heads):
1165 1180 their_heads = decodelist(heads)
1166 1181
1167 1182 with proto.mayberedirectstdio() as output:
1168 1183 try:
1169 1184 exchange.check_heads(repo, their_heads, 'preparing changes')
1170 1185 cleanup = lambda: None
1171 1186 try:
1172 1187 payload = proto.getpayload()
1173 1188 if repo.ui.configbool('server', 'streamunbundle'):
1174 1189 def cleanup():
1175 1190 # Ensure that the full payload is consumed, so
1176 1191 # that the connection doesn't contain trailing garbage.
1177 1192 for p in payload:
1178 1193 pass
1179 1194 fp = util.chunkbuffer(payload)
1180 1195 else:
1181 1196 # write bundle data to temporary file as it can be big
1182 1197 fp, tempname = None, None
1183 1198 def cleanup():
1184 1199 if fp:
1185 1200 fp.close()
1186 1201 if tempname:
1187 1202 os.unlink(tempname)
1188 1203 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1189 1204 repo.ui.debug('redirecting incoming bundle to %s\n' %
1190 1205 tempname)
1191 1206 fp = os.fdopen(fd, pycompat.sysstr('wb+'))
1192 1207 r = 0
1193 1208 for p in payload:
1194 1209 fp.write(p)
1195 1210 fp.seek(0)
1196 1211
1197 1212 gen = exchange.readbundle(repo.ui, fp, None)
1198 1213 if (isinstance(gen, changegroupmod.cg1unpacker)
1199 1214 and not bundle1allowed(repo, 'push')):
1200 1215 if proto.name == 'http-v1':
1201 1216 # need to special case http because stderr do not get to
1202 1217 # the http client on failed push so we need to abuse
1203 1218 # some other error type to make sure the message get to
1204 1219 # the user.
1205 1220 return wireprototypes.ooberror(bundle2required)
1206 1221 raise error.Abort(bundle2requiredmain,
1207 1222 hint=bundle2requiredhint)
1208 1223
1209 1224 r = exchange.unbundle(repo, gen, their_heads, 'serve',
1210 1225 proto.client())
1211 1226 if util.safehasattr(r, 'addpart'):
1212 1227 # The return looks streamable, we are in the bundle2 case
1213 1228 # and should return a stream.
1214 1229 return wireprototypes.streamreslegacy(gen=r.getchunks())
1215 1230 return wireprototypes.pushres(
1216 1231 r, output.getvalue() if output else '')
1217 1232
1218 1233 finally:
1219 1234 cleanup()
1220 1235
1221 1236 except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
1222 1237 # handle non-bundle2 case first
1223 1238 if not getattr(exc, 'duringunbundle2', False):
1224 1239 try:
1225 1240 raise
1226 1241 except error.Abort:
1227 1242 # The old code we moved used procutil.stderr directly.
1228 1243 # We did not change it to minimise code change.
1229 1244 # This need to be moved to something proper.
1230 1245 # Feel free to do it.
1231 1246 procutil.stderr.write("abort: %s\n" % exc)
1232 1247 if exc.hint is not None:
1233 1248 procutil.stderr.write("(%s)\n" % exc.hint)
1234 1249 procutil.stderr.flush()
1235 1250 return wireprototypes.pushres(
1236 1251 0, output.getvalue() if output else '')
1237 1252 except error.PushRaced:
1238 1253 return wireprototypes.pusherr(
1239 1254 pycompat.bytestr(exc),
1240 1255 output.getvalue() if output else '')
1241 1256
1242 1257 bundler = bundle2.bundle20(repo.ui)
1243 1258 for out in getattr(exc, '_bundle2salvagedoutput', ()):
1244 1259 bundler.addpart(out)
1245 1260 try:
1246 1261 try:
1247 1262 raise
1248 1263 except error.PushkeyFailed as exc:
1249 1264 # check client caps
1250 1265 remotecaps = getattr(exc, '_replycaps', None)
1251 1266 if (remotecaps is not None
1252 1267 and 'pushkey' not in remotecaps.get('error', ())):
1253 1268 # no support remote side, fallback to Abort handler.
1254 1269 raise
1255 1270 part = bundler.newpart('error:pushkey')
1256 1271 part.addparam('in-reply-to', exc.partid)
1257 1272 if exc.namespace is not None:
1258 1273 part.addparam('namespace', exc.namespace,
1259 1274 mandatory=False)
1260 1275 if exc.key is not None:
1261 1276 part.addparam('key', exc.key, mandatory=False)
1262 1277 if exc.new is not None:
1263 1278 part.addparam('new', exc.new, mandatory=False)
1264 1279 if exc.old is not None:
1265 1280 part.addparam('old', exc.old, mandatory=False)
1266 1281 if exc.ret is not None:
1267 1282 part.addparam('ret', exc.ret, mandatory=False)
1268 1283 except error.BundleValueError as exc:
1269 1284 errpart = bundler.newpart('error:unsupportedcontent')
1270 1285 if exc.parttype is not None:
1271 1286 errpart.addparam('parttype', exc.parttype)
1272 1287 if exc.params:
1273 1288 errpart.addparam('params', '\0'.join(exc.params))
1274 1289 except error.Abort as exc:
1275 1290 manargs = [('message', stringutil.forcebytestr(exc))]
1276 1291 advargs = []
1277 1292 if exc.hint is not None:
1278 1293 advargs.append(('hint', exc.hint))
1279 1294 bundler.addpart(bundle2.bundlepart('error:abort',
1280 1295 manargs, advargs))
1281 1296 except error.PushRaced as exc:
1282 1297 bundler.newpart('error:pushraced',
1283 1298 [('message', stringutil.forcebytestr(exc))])
1284 1299 return wireprototypes.streamreslegacy(gen=bundler.getchunks())
1285 1300
1286 1301 # Wire protocol version 2 commands only past this point.
1287 1302
1288 1303 def _capabilitiesv2(repo, proto):
1289 1304 """Obtain the set of capabilities for version 2 transports.
1290 1305
1291 1306 These capabilities are distinct from the capabilities for version 1
1292 1307 transports.
1293 1308 """
1294 1309 compression = []
1295 1310 for engine in supportedcompengines(repo.ui, util.SERVERROLE):
1296 1311 compression.append({
1297 1312 b'name': engine.wireprotosupport().name,
1298 1313 })
1299 1314
1300 1315 caps = {
1301 1316 'commands': {},
1302 1317 'compression': compression,
1303 1318 }
1304 1319
1305 1320 for command, entry in commandsv2.items():
1306 1321 caps['commands'][command] = {
1307 'args': sorted(entry.args.split()) if entry.args else [],
1322 'args': entry.args,
1308 1323 'permissions': [entry.permission],
1309 1324 }
1310 1325
1311 1326 return proto.addcapabilities(repo, caps)
1312 1327
1313 1328 @wireprotocommand('branchmap', permission='pull',
1314 1329 transportpolicy=POLICY_V2_ONLY)
1315 1330 def branchmapv2(repo, proto):
1316 1331 branchmap = {encoding.fromlocal(k): v
1317 1332 for k, v in repo.branchmap().iteritems()}
1318 1333
1319 1334 return wireprototypes.cborresponse(branchmap)
1320 1335
1321 1336 @wireprotocommand('capabilities', permission='pull',
1322 1337 transportpolicy=POLICY_V2_ONLY)
1323 1338 def capabilitiesv2(repo, proto):
1324 1339 caps = _capabilitiesv2(repo, proto)
1325 1340
1326 1341 return wireprototypes.cborresponse(caps)
1327 1342
1328 @wireprotocommand('heads', args='publiconly', permission='pull',
1343 @wireprotocommand('heads',
1344 args={
1345 'publiconly': False,
1346 },
1347 permission='pull',
1329 1348 transportpolicy=POLICY_V2_ONLY)
1330 1349 def headsv2(repo, proto, publiconly=False):
1331 1350 if publiconly:
1332 1351 repo = repo.filtered('immutable')
1333 1352
1334 1353 return wireprototypes.cborresponse(repo.heads())
1335 1354
1336 @wireprotocommand('known', 'nodes', permission='pull',
1355 @wireprotocommand('known',
1356 args={
1357 'nodes': [b'deadbeef'],
1358 },
1359 permission='pull',
1337 1360 transportpolicy=POLICY_V2_ONLY)
1338 1361 def knownv2(repo, proto, nodes=None):
1339 1362 nodes = nodes or []
1340 1363 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
1341 1364 return wireprototypes.cborresponse(result)
1342 1365
1343 @wireprotocommand('listkeys', 'namespace', permission='pull',
1366 @wireprotocommand('listkeys',
1367 args={
1368 'namespace': b'ns',
1369 },
1370 permission='pull',
1344 1371 transportpolicy=POLICY_V2_ONLY)
1345 1372 def listkeysv2(repo, proto, namespace=None):
1346 1373 keys = repo.listkeys(encoding.tolocal(namespace))
1347 1374 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
1348 1375 for k, v in keys.iteritems()}
1349 1376
1350 1377 return wireprototypes.cborresponse(keys)
@@ -1,1077 +1,1078 b''
1 1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 3 #
4 4 # This software may be used and distributed according to the terms of the
5 5 # GNU General Public License version 2 or any later version.
6 6
7 7 from __future__ import absolute_import
8 8
9 9 import contextlib
10 10 import struct
11 11 import sys
12 12 import threading
13 13
14 14 from .i18n import _
15 15 from .thirdparty import (
16 16 cbor,
17 17 )
18 18 from .thirdparty.zope import (
19 19 interface as zi,
20 20 )
21 21 from . import (
22 22 encoding,
23 23 error,
24 24 hook,
25 25 pycompat,
26 26 util,
27 27 wireproto,
28 28 wireprotoframing,
29 29 wireprototypes,
30 30 )
31 31 from .utils import (
32 32 procutil,
33 33 )
34 34
35 35 stringio = util.stringio
36 36
37 37 urlerr = util.urlerr
38 38 urlreq = util.urlreq
39 39
40 40 HTTP_OK = 200
41 41
42 42 HGTYPE = 'application/mercurial-0.1'
43 43 HGTYPE2 = 'application/mercurial-0.2'
44 44 HGERRTYPE = 'application/hg-error'
45 45 FRAMINGTYPE = b'application/mercurial-exp-framing-0003'
46 46
47 47 HTTPV2 = wireprototypes.HTTPV2
48 48 SSHV1 = wireprototypes.SSHV1
49 49 SSHV2 = wireprototypes.SSHV2
50 50
51 51 def decodevaluefromheaders(req, headerprefix):
52 52 """Decode a long value from multiple HTTP request headers.
53 53
54 54 Returns the value as a bytes, not a str.
55 55 """
56 56 chunks = []
57 57 i = 1
58 58 while True:
59 59 v = req.headers.get(b'%s-%d' % (headerprefix, i))
60 60 if v is None:
61 61 break
62 62 chunks.append(pycompat.bytesurl(v))
63 63 i += 1
64 64
65 65 return ''.join(chunks)
66 66
67 67 @zi.implementer(wireprototypes.baseprotocolhandler)
68 68 class httpv1protocolhandler(object):
69 69 def __init__(self, req, ui, checkperm):
70 70 self._req = req
71 71 self._ui = ui
72 72 self._checkperm = checkperm
73 73 self._protocaps = None
74 74
75 75 @property
76 76 def name(self):
77 77 return 'http-v1'
78 78
79 79 def getargs(self, args):
80 80 knownargs = self._args()
81 81 data = {}
82 82 keys = args.split()
83 83 for k in keys:
84 84 if k == '*':
85 85 star = {}
86 86 for key in knownargs.keys():
87 87 if key != 'cmd' and key not in keys:
88 88 star[key] = knownargs[key][0]
89 89 data['*'] = star
90 90 else:
91 91 data[k] = knownargs[k][0]
92 92 return [data[k] for k in keys]
93 93
94 94 def _args(self):
95 95 args = self._req.qsparams.asdictoflists()
96 96 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
97 97 if postlen:
98 98 args.update(urlreq.parseqs(
99 99 self._req.bodyfh.read(postlen), keep_blank_values=True))
100 100 return args
101 101
102 102 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
103 103 args.update(urlreq.parseqs(argvalue, keep_blank_values=True))
104 104 return args
105 105
106 106 def getprotocaps(self):
107 107 if self._protocaps is None:
108 108 value = decodevaluefromheaders(self._req, r'X-HgProto')
109 109 self._protocaps = set(value.split(' '))
110 110 return self._protocaps
111 111
112 112 def getpayload(self):
113 113 # Existing clients *always* send Content-Length.
114 114 length = int(self._req.headers[b'Content-Length'])
115 115
116 116 # If httppostargs is used, we need to read Content-Length
117 117 # minus the amount that was consumed by args.
118 118 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
119 119 return util.filechunkiter(self._req.bodyfh, limit=length)
120 120
121 121 @contextlib.contextmanager
122 122 def mayberedirectstdio(self):
123 123 oldout = self._ui.fout
124 124 olderr = self._ui.ferr
125 125
126 126 out = util.stringio()
127 127
128 128 try:
129 129 self._ui.fout = out
130 130 self._ui.ferr = out
131 131 yield out
132 132 finally:
133 133 self._ui.fout = oldout
134 134 self._ui.ferr = olderr
135 135
136 136 def client(self):
137 137 return 'remote:%s:%s:%s' % (
138 138 self._req.urlscheme,
139 139 urlreq.quote(self._req.remotehost or ''),
140 140 urlreq.quote(self._req.remoteuser or ''))
141 141
142 142 def addcapabilities(self, repo, caps):
143 143 caps.append(b'batch')
144 144
145 145 caps.append('httpheader=%d' %
146 146 repo.ui.configint('server', 'maxhttpheaderlen'))
147 147 if repo.ui.configbool('experimental', 'httppostargs'):
148 148 caps.append('httppostargs')
149 149
150 150 # FUTURE advertise 0.2rx once support is implemented
151 151 # FUTURE advertise minrx and mintx after consulting config option
152 152 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
153 153
154 154 compengines = wireproto.supportedcompengines(repo.ui, util.SERVERROLE)
155 155 if compengines:
156 156 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
157 157 for e in compengines)
158 158 caps.append('compression=%s' % comptypes)
159 159
160 160 return caps
161 161
162 162 def checkperm(self, perm):
163 163 return self._checkperm(perm)
164 164
165 165 # This method exists mostly so that extensions like remotefilelog can
166 166 # disable a kludgey legacy method only over http. As of early 2018,
167 167 # there are no other known users, so with any luck we can discard this
168 168 # hook if remotefilelog becomes a first-party extension.
169 169 def iscmd(cmd):
170 170 return cmd in wireproto.commands
171 171
172 172 def handlewsgirequest(rctx, req, res, checkperm):
173 173 """Possibly process a wire protocol request.
174 174
175 175 If the current request is a wire protocol request, the request is
176 176 processed by this function.
177 177
178 178 ``req`` is a ``parsedrequest`` instance.
179 179 ``res`` is a ``wsgiresponse`` instance.
180 180
181 181 Returns a bool indicating if the request was serviced. If set, the caller
182 182 should stop processing the request, as a response has already been issued.
183 183 """
184 184 # Avoid cycle involving hg module.
185 185 from .hgweb import common as hgwebcommon
186 186
187 187 repo = rctx.repo
188 188
189 189 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
190 190 # string parameter. If it isn't present, this isn't a wire protocol
191 191 # request.
192 192 if 'cmd' not in req.qsparams:
193 193 return False
194 194
195 195 cmd = req.qsparams['cmd']
196 196
197 197 # The "cmd" request parameter is used by both the wire protocol and hgweb.
198 198 # While not all wire protocol commands are available for all transports,
199 199 # if we see a "cmd" value that resembles a known wire protocol command, we
200 200 # route it to a protocol handler. This is better than routing possible
201 201 # wire protocol requests to hgweb because it prevents hgweb from using
202 202 # known wire protocol commands and it is less confusing for machine
203 203 # clients.
204 204 if not iscmd(cmd):
205 205 return False
206 206
207 207 # The "cmd" query string argument is only valid on the root path of the
208 208 # repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
209 209 # like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
210 210 # in this case. We send an HTTP 404 for backwards compatibility reasons.
211 211 if req.dispatchpath:
212 212 res.status = hgwebcommon.statusmessage(404)
213 213 res.headers['Content-Type'] = HGTYPE
214 214 # TODO This is not a good response to issue for this request. This
215 215 # is mostly for BC for now.
216 216 res.setbodybytes('0\n%s\n' % b'Not Found')
217 217 return True
218 218
219 219 proto = httpv1protocolhandler(req, repo.ui,
220 220 lambda perm: checkperm(rctx, req, perm))
221 221
222 222 # The permissions checker should be the only thing that can raise an
223 223 # ErrorResponse. It is kind of a layer violation to catch an hgweb
224 224 # exception here. So consider refactoring into a exception type that
225 225 # is associated with the wire protocol.
226 226 try:
227 227 _callhttp(repo, req, res, proto, cmd)
228 228 except hgwebcommon.ErrorResponse as e:
229 229 for k, v in e.headers:
230 230 res.headers[k] = v
231 231 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
232 232 # TODO This response body assumes the failed command was
233 233 # "unbundle." That assumption is not always valid.
234 234 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
235 235
236 236 return True
237 237
238 238 def handlewsgiapirequest(rctx, req, res, checkperm):
239 239 """Handle requests to /api/*."""
240 240 assert req.dispatchparts[0] == b'api'
241 241
242 242 repo = rctx.repo
243 243
244 244 # This whole URL space is experimental for now. But we want to
245 245 # reserve the URL space. So, 404 all URLs if the feature isn't enabled.
246 246 if not repo.ui.configbool('experimental', 'web.apiserver'):
247 247 res.status = b'404 Not Found'
248 248 res.headers[b'Content-Type'] = b'text/plain'
249 249 res.setbodybytes(_('Experimental API server endpoint not enabled'))
250 250 return
251 251
252 252 # The URL space is /api/<protocol>/*. The structure of URLs under varies
253 253 # by <protocol>.
254 254
255 255 # Registered APIs are made available via config options of the name of
256 256 # the protocol.
257 257 availableapis = set()
258 258 for k, v in API_HANDLERS.items():
259 259 section, option = v['config']
260 260 if repo.ui.configbool(section, option):
261 261 availableapis.add(k)
262 262
263 263 # Requests to /api/ list available APIs.
264 264 if req.dispatchparts == [b'api']:
265 265 res.status = b'200 OK'
266 266 res.headers[b'Content-Type'] = b'text/plain'
267 267 lines = [_('APIs can be accessed at /api/<name>, where <name> can be '
268 268 'one of the following:\n')]
269 269 if availableapis:
270 270 lines.extend(sorted(availableapis))
271 271 else:
272 272 lines.append(_('(no available APIs)\n'))
273 273 res.setbodybytes(b'\n'.join(lines))
274 274 return
275 275
276 276 proto = req.dispatchparts[1]
277 277
278 278 if proto not in API_HANDLERS:
279 279 res.status = b'404 Not Found'
280 280 res.headers[b'Content-Type'] = b'text/plain'
281 281 res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % (
282 282 proto, b', '.join(sorted(availableapis))))
283 283 return
284 284
285 285 if proto not in availableapis:
286 286 res.status = b'404 Not Found'
287 287 res.headers[b'Content-Type'] = b'text/plain'
288 288 res.setbodybytes(_('API %s not enabled\n') % proto)
289 289 return
290 290
291 291 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm,
292 292 req.dispatchparts[2:])
293 293
294 294 def _handlehttpv2request(rctx, req, res, checkperm, urlparts):
295 295 from .hgweb import common as hgwebcommon
296 296
297 297 # URL space looks like: <permissions>/<command>, where <permission> can
298 298 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
299 299
300 300 # Root URL does nothing meaningful... yet.
301 301 if not urlparts:
302 302 res.status = b'200 OK'
303 303 res.headers[b'Content-Type'] = b'text/plain'
304 304 res.setbodybytes(_('HTTP version 2 API handler'))
305 305 return
306 306
307 307 if len(urlparts) == 1:
308 308 res.status = b'404 Not Found'
309 309 res.headers[b'Content-Type'] = b'text/plain'
310 310 res.setbodybytes(_('do not know how to process %s\n') %
311 311 req.dispatchpath)
312 312 return
313 313
314 314 permission, command = urlparts[0:2]
315 315
316 316 if permission not in (b'ro', b'rw'):
317 317 res.status = b'404 Not Found'
318 318 res.headers[b'Content-Type'] = b'text/plain'
319 319 res.setbodybytes(_('unknown permission: %s') % permission)
320 320 return
321 321
322 322 if req.method != 'POST':
323 323 res.status = b'405 Method Not Allowed'
324 324 res.headers[b'Allow'] = b'POST'
325 325 res.setbodybytes(_('commands require POST requests'))
326 326 return
327 327
328 328 # At some point we'll want to use our own API instead of recycling the
329 329 # behavior of version 1 of the wire protocol...
330 330 # TODO return reasonable responses - not responses that overload the
331 331 # HTTP status line message for error reporting.
332 332 try:
333 333 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
334 334 except hgwebcommon.ErrorResponse as e:
335 335 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
336 336 for k, v in e.headers:
337 337 res.headers[k] = v
338 338 res.setbodybytes('permission denied')
339 339 return
340 340
341 341 # We have a special endpoint to reflect the request back at the client.
342 342 if command == b'debugreflect':
343 343 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
344 344 return
345 345
346 346 # Extra commands that we handle that aren't really wire protocol
347 347 # commands. Think extra hard before making this hackery available to
348 348 # extension.
349 349 extracommands = {'multirequest'}
350 350
351 351 if command not in wireproto.commandsv2 and command not in extracommands:
352 352 res.status = b'404 Not Found'
353 353 res.headers[b'Content-Type'] = b'text/plain'
354 354 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
355 355 return
356 356
357 357 repo = rctx.repo
358 358 ui = repo.ui
359 359
360 360 proto = httpv2protocolhandler(req, ui)
361 361
362 362 if (not wireproto.commandsv2.commandavailable(command, proto)
363 363 and command not in extracommands):
364 364 res.status = b'404 Not Found'
365 365 res.headers[b'Content-Type'] = b'text/plain'
366 366 res.setbodybytes(_('invalid wire protocol command: %s') % command)
367 367 return
368 368
369 369 # TODO consider cases where proxies may add additional Accept headers.
370 370 if req.headers.get(b'Accept') != FRAMINGTYPE:
371 371 res.status = b'406 Not Acceptable'
372 372 res.headers[b'Content-Type'] = b'text/plain'
373 373 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
374 374 % FRAMINGTYPE)
375 375 return
376 376
377 377 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
378 378 res.status = b'415 Unsupported Media Type'
379 379 # TODO we should send a response with appropriate media type,
380 380 # since client does Accept it.
381 381 res.headers[b'Content-Type'] = b'text/plain'
382 382 res.setbodybytes(_('client MUST send Content-Type header with '
383 383 'value: %s\n') % FRAMINGTYPE)
384 384 return
385 385
386 386 _processhttpv2request(ui, repo, req, res, permission, command, proto)
387 387
388 388 def _processhttpv2reflectrequest(ui, repo, req, res):
389 389 """Reads unified frame protocol request and dumps out state to client.
390 390
391 391 This special endpoint can be used to help debug the wire protocol.
392 392
393 393 Instead of routing the request through the normal dispatch mechanism,
394 394 we instead read all frames, decode them, and feed them into our state
395 395 tracker. We then dump the log of all that activity back out to the
396 396 client.
397 397 """
398 398 import json
399 399
400 400 # Reflection APIs have a history of being abused, accidentally disclosing
401 401 # sensitive data, etc. So we have a config knob.
402 402 if not ui.configbool('experimental', 'web.api.debugreflect'):
403 403 res.status = b'404 Not Found'
404 404 res.headers[b'Content-Type'] = b'text/plain'
405 405 res.setbodybytes(_('debugreflect service not available'))
406 406 return
407 407
408 408 # We assume we have a unified framing protocol request body.
409 409
410 410 reactor = wireprotoframing.serverreactor()
411 411 states = []
412 412
413 413 while True:
414 414 frame = wireprotoframing.readframe(req.bodyfh)
415 415
416 416 if not frame:
417 417 states.append(b'received: <no frame>')
418 418 break
419 419
420 420 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
421 421 frame.requestid,
422 422 frame.payload))
423 423
424 424 action, meta = reactor.onframerecv(frame)
425 425 states.append(json.dumps((action, meta), sort_keys=True,
426 426 separators=(', ', ': ')))
427 427
428 428 action, meta = reactor.oninputeof()
429 429 meta['action'] = action
430 430 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
431 431
432 432 res.status = b'200 OK'
433 433 res.headers[b'Content-Type'] = b'text/plain'
434 434 res.setbodybytes(b'\n'.join(states))
435 435
436 436 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
437 437 """Post-validation handler for HTTPv2 requests.
438 438
439 439 Called when the HTTP request contains unified frame-based protocol
440 440 frames for evaluation.
441 441 """
442 442 # TODO Some HTTP clients are full duplex and can receive data before
443 443 # the entire request is transmitted. Figure out a way to indicate support
444 444 # for that so we can opt into full duplex mode.
445 445 reactor = wireprotoframing.serverreactor(deferoutput=True)
446 446 seencommand = False
447 447
448 448 outstream = reactor.makeoutputstream()
449 449
450 450 while True:
451 451 frame = wireprotoframing.readframe(req.bodyfh)
452 452 if not frame:
453 453 break
454 454
455 455 action, meta = reactor.onframerecv(frame)
456 456
457 457 if action == 'wantframe':
458 458 # Need more data before we can do anything.
459 459 continue
460 460 elif action == 'runcommand':
461 461 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
462 462 reqcommand, reactor, outstream,
463 463 meta, issubsequent=seencommand)
464 464
465 465 if sentoutput:
466 466 return
467 467
468 468 seencommand = True
469 469
470 470 elif action == 'error':
471 471 # TODO define proper error mechanism.
472 472 res.status = b'200 OK'
473 473 res.headers[b'Content-Type'] = b'text/plain'
474 474 res.setbodybytes(meta['message'] + b'\n')
475 475 return
476 476 else:
477 477 raise error.ProgrammingError(
478 478 'unhandled action from frame processor: %s' % action)
479 479
480 480 action, meta = reactor.oninputeof()
481 481 if action == 'sendframes':
482 482 # We assume we haven't started sending the response yet. If we're
483 483 # wrong, the response type will raise an exception.
484 484 res.status = b'200 OK'
485 485 res.headers[b'Content-Type'] = FRAMINGTYPE
486 486 res.setbodygen(meta['framegen'])
487 487 elif action == 'noop':
488 488 pass
489 489 else:
490 490 raise error.ProgrammingError('unhandled action from frame processor: %s'
491 491 % action)
492 492
493 493 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
494 494 outstream, command, issubsequent):
495 495 """Dispatch a wire protocol command made from HTTPv2 requests.
496 496
497 497 The authenticated permission (``authedperm``) along with the original
498 498 command from the URL (``reqcommand``) are passed in.
499 499 """
500 500 # We already validated that the session has permissions to perform the
501 501 # actions in ``authedperm``. In the unified frame protocol, the canonical
502 502 # command to run is expressed in a frame. However, the URL also requested
503 503 # to run a specific command. We need to be careful that the command we
504 504 # run doesn't have permissions requirements greater than what was granted
505 505 # by ``authedperm``.
506 506 #
507 507 # Our rule for this is we only allow one command per HTTP request and
508 508 # that command must match the command in the URL. However, we make
509 509 # an exception for the ``multirequest`` URL. This URL is allowed to
510 510 # execute multiple commands. We double check permissions of each command
511 511 # as it is invoked to ensure there is no privilege escalation.
512 512 # TODO consider allowing multiple commands to regular command URLs
513 513 # iff each command is the same.
514 514
515 515 proto = httpv2protocolhandler(req, ui, args=command['args'])
516 516
517 517 if reqcommand == b'multirequest':
518 518 if not wireproto.commandsv2.commandavailable(command['command'], proto):
519 519 # TODO proper error mechanism
520 520 res.status = b'200 OK'
521 521 res.headers[b'Content-Type'] = b'text/plain'
522 522 res.setbodybytes(_('wire protocol command not available: %s') %
523 523 command['command'])
524 524 return True
525 525
526 526 # TODO don't use assert here, since it may be elided by -O.
527 527 assert authedperm in (b'ro', b'rw')
528 528 wirecommand = wireproto.commandsv2[command['command']]
529 529 assert wirecommand.permission in ('push', 'pull')
530 530
531 531 if authedperm == b'ro' and wirecommand.permission != 'pull':
532 532 # TODO proper error mechanism
533 533 res.status = b'403 Forbidden'
534 534 res.headers[b'Content-Type'] = b'text/plain'
535 535 res.setbodybytes(_('insufficient permissions to execute '
536 536 'command: %s') % command['command'])
537 537 return True
538 538
539 539 # TODO should we also call checkperm() here? Maybe not if we're going
540 540 # to overhaul that API. The granted scope from the URL check should
541 541 # be good enough.
542 542
543 543 else:
544 544 # Don't allow multiple commands outside of ``multirequest`` URL.
545 545 if issubsequent:
546 546 # TODO proper error mechanism
547 547 res.status = b'200 OK'
548 548 res.headers[b'Content-Type'] = b'text/plain'
549 549 res.setbodybytes(_('multiple commands cannot be issued to this '
550 550 'URL'))
551 551 return True
552 552
553 553 if reqcommand != command['command']:
554 554 # TODO define proper error mechanism
555 555 res.status = b'200 OK'
556 556 res.headers[b'Content-Type'] = b'text/plain'
557 557 res.setbodybytes(_('command in frame must match command in URL'))
558 558 return True
559 559
560 560 rsp = wireproto.dispatch(repo, proto, command['command'])
561 561
562 562 res.status = b'200 OK'
563 563 res.headers[b'Content-Type'] = FRAMINGTYPE
564 564
565 565 if isinstance(rsp, wireprototypes.bytesresponse):
566 566 action, meta = reactor.onbytesresponseready(outstream,
567 567 command['requestid'],
568 568 rsp.data)
569 569 elif isinstance(rsp, wireprototypes.cborresponse):
570 570 encoded = cbor.dumps(rsp.value, canonical=True)
571 571 action, meta = reactor.onbytesresponseready(outstream,
572 572 command['requestid'],
573 573 encoded,
574 574 iscbor=True)
575 575 else:
576 576 action, meta = reactor.onapplicationerror(
577 577 _('unhandled response type from wire proto command'))
578 578
579 579 if action == 'sendframes':
580 580 res.setbodygen(meta['framegen'])
581 581 return True
582 582 elif action == 'noop':
583 583 return False
584 584 else:
585 585 raise error.ProgrammingError('unhandled event from reactor: %s' %
586 586 action)
587 587
588 588 # Maps API name to metadata so custom API can be registered.
589 589 API_HANDLERS = {
590 590 HTTPV2: {
591 591 'config': ('experimental', 'web.api.http-v2'),
592 592 'handler': _handlehttpv2request,
593 593 },
594 594 }
595 595
596 596 @zi.implementer(wireprototypes.baseprotocolhandler)
597 597 class httpv2protocolhandler(object):
598 598 def __init__(self, req, ui, args=None):
599 599 self._req = req
600 600 self._ui = ui
601 601 self._args = args
602 602
603 603 @property
604 604 def name(self):
605 605 return HTTPV2
606 606
607 607 def getargs(self, args):
608 608 data = {}
609 for k in args.split():
609 for k, typ in args.items():
610 610 if k == '*':
611 611 raise NotImplementedError('do not support * args')
612 612 elif k in self._args:
613 # TODO consider validating value types.
613 614 data[k] = self._args[k]
614 615
615 616 return data
616 617
617 618 def getprotocaps(self):
618 619 # Protocol capabilities are currently not implemented for HTTP V2.
619 620 return set()
620 621
621 622 def getpayload(self):
622 623 raise NotImplementedError
623 624
624 625 @contextlib.contextmanager
625 626 def mayberedirectstdio(self):
626 627 raise NotImplementedError
627 628
628 629 def client(self):
629 630 raise NotImplementedError
630 631
631 632 def addcapabilities(self, repo, caps):
632 633 return caps
633 634
634 635 def checkperm(self, perm):
635 636 raise NotImplementedError
636 637
637 638 def _httpresponsetype(ui, proto, prefer_uncompressed):
638 639 """Determine the appropriate response type and compression settings.
639 640
640 641 Returns a tuple of (mediatype, compengine, engineopts).
641 642 """
642 643 # Determine the response media type and compression engine based
643 644 # on the request parameters.
644 645
645 646 if '0.2' in proto.getprotocaps():
646 647 # All clients are expected to support uncompressed data.
647 648 if prefer_uncompressed:
648 649 return HGTYPE2, util._noopengine(), {}
649 650
650 651 # Now find an agreed upon compression format.
651 652 compformats = wireproto.clientcompressionsupport(proto)
652 653 for engine in wireproto.supportedcompengines(ui, util.SERVERROLE):
653 654 if engine.wireprotosupport().name in compformats:
654 655 opts = {}
655 656 level = ui.configint('server', '%slevel' % engine.name())
656 657 if level is not None:
657 658 opts['level'] = level
658 659
659 660 return HGTYPE2, engine, opts
660 661
661 662 # No mutually supported compression format. Fall back to the
662 663 # legacy protocol.
663 664
664 665 # Don't allow untrusted settings because disabling compression or
665 666 # setting a very high compression level could lead to flooding
666 667 # the server's network or CPU.
667 668 opts = {'level': ui.configint('server', 'zliblevel')}
668 669 return HGTYPE, util.compengines['zlib'], opts
669 670
670 671 def _callhttp(repo, req, res, proto, cmd):
671 672 # Avoid cycle involving hg module.
672 673 from .hgweb import common as hgwebcommon
673 674
674 675 def genversion2(gen, engine, engineopts):
675 676 # application/mercurial-0.2 always sends a payload header
676 677 # identifying the compression engine.
677 678 name = engine.wireprotosupport().name
678 679 assert 0 < len(name) < 256
679 680 yield struct.pack('B', len(name))
680 681 yield name
681 682
682 683 for chunk in gen:
683 684 yield chunk
684 685
685 686 def setresponse(code, contenttype, bodybytes=None, bodygen=None):
686 687 if code == HTTP_OK:
687 688 res.status = '200 Script output follows'
688 689 else:
689 690 res.status = hgwebcommon.statusmessage(code)
690 691
691 692 res.headers['Content-Type'] = contenttype
692 693
693 694 if bodybytes is not None:
694 695 res.setbodybytes(bodybytes)
695 696 if bodygen is not None:
696 697 res.setbodygen(bodygen)
697 698
698 699 if not wireproto.commands.commandavailable(cmd, proto):
699 700 setresponse(HTTP_OK, HGERRTYPE,
700 701 _('requested wire protocol command is not available over '
701 702 'HTTP'))
702 703 return
703 704
704 705 proto.checkperm(wireproto.commands[cmd].permission)
705 706
706 707 rsp = wireproto.dispatch(repo, proto, cmd)
707 708
708 709 if isinstance(rsp, bytes):
709 710 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
710 711 elif isinstance(rsp, wireprototypes.bytesresponse):
711 712 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp.data)
712 713 elif isinstance(rsp, wireprototypes.streamreslegacy):
713 714 setresponse(HTTP_OK, HGTYPE, bodygen=rsp.gen)
714 715 elif isinstance(rsp, wireprototypes.streamres):
715 716 gen = rsp.gen
716 717
717 718 # This code for compression should not be streamres specific. It
718 719 # is here because we only compress streamres at the moment.
719 720 mediatype, engine, engineopts = _httpresponsetype(
720 721 repo.ui, proto, rsp.prefer_uncompressed)
721 722 gen = engine.compressstream(gen, engineopts)
722 723
723 724 if mediatype == HGTYPE2:
724 725 gen = genversion2(gen, engine, engineopts)
725 726
726 727 setresponse(HTTP_OK, mediatype, bodygen=gen)
727 728 elif isinstance(rsp, wireprototypes.pushres):
728 729 rsp = '%d\n%s' % (rsp.res, rsp.output)
729 730 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
730 731 elif isinstance(rsp, wireprototypes.pusherr):
731 732 rsp = '0\n%s\n' % rsp.res
732 733 res.drain = True
733 734 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
734 735 elif isinstance(rsp, wireprototypes.ooberror):
735 736 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
736 737 else:
737 738 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
738 739
739 740 def _sshv1respondbytes(fout, value):
740 741 """Send a bytes response for protocol version 1."""
741 742 fout.write('%d\n' % len(value))
742 743 fout.write(value)
743 744 fout.flush()
744 745
745 746 def _sshv1respondstream(fout, source):
746 747 write = fout.write
747 748 for chunk in source.gen:
748 749 write(chunk)
749 750 fout.flush()
750 751
751 752 def _sshv1respondooberror(fout, ferr, rsp):
752 753 ferr.write(b'%s\n-\n' % rsp)
753 754 ferr.flush()
754 755 fout.write(b'\n')
755 756 fout.flush()
756 757
757 758 @zi.implementer(wireprototypes.baseprotocolhandler)
758 759 class sshv1protocolhandler(object):
759 760 """Handler for requests services via version 1 of SSH protocol."""
760 761 def __init__(self, ui, fin, fout):
761 762 self._ui = ui
762 763 self._fin = fin
763 764 self._fout = fout
764 765 self._protocaps = set()
765 766
766 767 @property
767 768 def name(self):
768 769 return wireprototypes.SSHV1
769 770
770 771 def getargs(self, args):
771 772 data = {}
772 773 keys = args.split()
773 774 for n in xrange(len(keys)):
774 775 argline = self._fin.readline()[:-1]
775 776 arg, l = argline.split()
776 777 if arg not in keys:
777 778 raise error.Abort(_("unexpected parameter %r") % arg)
778 779 if arg == '*':
779 780 star = {}
780 781 for k in xrange(int(l)):
781 782 argline = self._fin.readline()[:-1]
782 783 arg, l = argline.split()
783 784 val = self._fin.read(int(l))
784 785 star[arg] = val
785 786 data['*'] = star
786 787 else:
787 788 val = self._fin.read(int(l))
788 789 data[arg] = val
789 790 return [data[k] for k in keys]
790 791
791 792 def getprotocaps(self):
792 793 return self._protocaps
793 794
794 795 def getpayload(self):
795 796 # We initially send an empty response. This tells the client it is
796 797 # OK to start sending data. If a client sees any other response, it
797 798 # interprets it as an error.
798 799 _sshv1respondbytes(self._fout, b'')
799 800
800 801 # The file is in the form:
801 802 #
802 803 # <chunk size>\n<chunk>
803 804 # ...
804 805 # 0\n
805 806 count = int(self._fin.readline())
806 807 while count:
807 808 yield self._fin.read(count)
808 809 count = int(self._fin.readline())
809 810
810 811 @contextlib.contextmanager
811 812 def mayberedirectstdio(self):
812 813 yield None
813 814
814 815 def client(self):
815 816 client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
816 817 return 'remote:ssh:' + client
817 818
818 819 def addcapabilities(self, repo, caps):
819 820 if self.name == wireprototypes.SSHV1:
820 821 caps.append(b'protocaps')
821 822 caps.append(b'batch')
822 823 return caps
823 824
824 825 def checkperm(self, perm):
825 826 pass
826 827
827 828 class sshv2protocolhandler(sshv1protocolhandler):
828 829 """Protocol handler for version 2 of the SSH protocol."""
829 830
830 831 @property
831 832 def name(self):
832 833 return wireprototypes.SSHV2
833 834
834 835 def addcapabilities(self, repo, caps):
835 836 return caps
836 837
837 838 def _runsshserver(ui, repo, fin, fout, ev):
838 839 # This function operates like a state machine of sorts. The following
839 840 # states are defined:
840 841 #
841 842 # protov1-serving
842 843 # Server is in protocol version 1 serving mode. Commands arrive on
843 844 # new lines. These commands are processed in this state, one command
844 845 # after the other.
845 846 #
846 847 # protov2-serving
847 848 # Server is in protocol version 2 serving mode.
848 849 #
849 850 # upgrade-initial
850 851 # The server is going to process an upgrade request.
851 852 #
852 853 # upgrade-v2-filter-legacy-handshake
853 854 # The protocol is being upgraded to version 2. The server is expecting
854 855 # the legacy handshake from version 1.
855 856 #
856 857 # upgrade-v2-finish
857 858 # The upgrade to version 2 of the protocol is imminent.
858 859 #
859 860 # shutdown
860 861 # The server is shutting down, possibly in reaction to a client event.
861 862 #
862 863 # And here are their transitions:
863 864 #
864 865 # protov1-serving -> shutdown
865 866 # When server receives an empty request or encounters another
866 867 # error.
867 868 #
868 869 # protov1-serving -> upgrade-initial
869 870 # An upgrade request line was seen.
870 871 #
871 872 # upgrade-initial -> upgrade-v2-filter-legacy-handshake
872 873 # Upgrade to version 2 in progress. Server is expecting to
873 874 # process a legacy handshake.
874 875 #
875 876 # upgrade-v2-filter-legacy-handshake -> shutdown
876 877 # Client did not fulfill upgrade handshake requirements.
877 878 #
878 879 # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
879 880 # Client fulfilled version 2 upgrade requirements. Finishing that
880 881 # upgrade.
881 882 #
882 883 # upgrade-v2-finish -> protov2-serving
883 884 # Protocol upgrade to version 2 complete. Server can now speak protocol
884 885 # version 2.
885 886 #
886 887 # protov2-serving -> protov1-serving
887 888 # Ths happens by default since protocol version 2 is the same as
888 889 # version 1 except for the handshake.
889 890
890 891 state = 'protov1-serving'
891 892 proto = sshv1protocolhandler(ui, fin, fout)
892 893 protoswitched = False
893 894
894 895 while not ev.is_set():
895 896 if state == 'protov1-serving':
896 897 # Commands are issued on new lines.
897 898 request = fin.readline()[:-1]
898 899
899 900 # Empty lines signal to terminate the connection.
900 901 if not request:
901 902 state = 'shutdown'
902 903 continue
903 904
904 905 # It looks like a protocol upgrade request. Transition state to
905 906 # handle it.
906 907 if request.startswith(b'upgrade '):
907 908 if protoswitched:
908 909 _sshv1respondooberror(fout, ui.ferr,
909 910 b'cannot upgrade protocols multiple '
910 911 b'times')
911 912 state = 'shutdown'
912 913 continue
913 914
914 915 state = 'upgrade-initial'
915 916 continue
916 917
917 918 available = wireproto.commands.commandavailable(request, proto)
918 919
919 920 # This command isn't available. Send an empty response and go
920 921 # back to waiting for a new command.
921 922 if not available:
922 923 _sshv1respondbytes(fout, b'')
923 924 continue
924 925
925 926 rsp = wireproto.dispatch(repo, proto, request)
926 927
927 928 if isinstance(rsp, bytes):
928 929 _sshv1respondbytes(fout, rsp)
929 930 elif isinstance(rsp, wireprototypes.bytesresponse):
930 931 _sshv1respondbytes(fout, rsp.data)
931 932 elif isinstance(rsp, wireprototypes.streamres):
932 933 _sshv1respondstream(fout, rsp)
933 934 elif isinstance(rsp, wireprototypes.streamreslegacy):
934 935 _sshv1respondstream(fout, rsp)
935 936 elif isinstance(rsp, wireprototypes.pushres):
936 937 _sshv1respondbytes(fout, b'')
937 938 _sshv1respondbytes(fout, b'%d' % rsp.res)
938 939 elif isinstance(rsp, wireprototypes.pusherr):
939 940 _sshv1respondbytes(fout, rsp.res)
940 941 elif isinstance(rsp, wireprototypes.ooberror):
941 942 _sshv1respondooberror(fout, ui.ferr, rsp.message)
942 943 else:
943 944 raise error.ProgrammingError('unhandled response type from '
944 945 'wire protocol command: %s' % rsp)
945 946
946 947 # For now, protocol version 2 serving just goes back to version 1.
947 948 elif state == 'protov2-serving':
948 949 state = 'protov1-serving'
949 950 continue
950 951
951 952 elif state == 'upgrade-initial':
952 953 # We should never transition into this state if we've switched
953 954 # protocols.
954 955 assert not protoswitched
955 956 assert proto.name == wireprototypes.SSHV1
956 957
957 958 # Expected: upgrade <token> <capabilities>
958 959 # If we get something else, the request is malformed. It could be
959 960 # from a future client that has altered the upgrade line content.
960 961 # We treat this as an unknown command.
961 962 try:
962 963 token, caps = request.split(b' ')[1:]
963 964 except ValueError:
964 965 _sshv1respondbytes(fout, b'')
965 966 state = 'protov1-serving'
966 967 continue
967 968
968 969 # Send empty response if we don't support upgrading protocols.
969 970 if not ui.configbool('experimental', 'sshserver.support-v2'):
970 971 _sshv1respondbytes(fout, b'')
971 972 state = 'protov1-serving'
972 973 continue
973 974
974 975 try:
975 976 caps = urlreq.parseqs(caps)
976 977 except ValueError:
977 978 _sshv1respondbytes(fout, b'')
978 979 state = 'protov1-serving'
979 980 continue
980 981
981 982 # We don't see an upgrade request to protocol version 2. Ignore
982 983 # the upgrade request.
983 984 wantedprotos = caps.get(b'proto', [b''])[0]
984 985 if SSHV2 not in wantedprotos:
985 986 _sshv1respondbytes(fout, b'')
986 987 state = 'protov1-serving'
987 988 continue
988 989
989 990 # It looks like we can honor this upgrade request to protocol 2.
990 991 # Filter the rest of the handshake protocol request lines.
991 992 state = 'upgrade-v2-filter-legacy-handshake'
992 993 continue
993 994
994 995 elif state == 'upgrade-v2-filter-legacy-handshake':
995 996 # Client should have sent legacy handshake after an ``upgrade``
996 997 # request. Expected lines:
997 998 #
998 999 # hello
999 1000 # between
1000 1001 # pairs 81
1001 1002 # 0000...-0000...
1002 1003
1003 1004 ok = True
1004 1005 for line in (b'hello', b'between', b'pairs 81'):
1005 1006 request = fin.readline()[:-1]
1006 1007
1007 1008 if request != line:
1008 1009 _sshv1respondooberror(fout, ui.ferr,
1009 1010 b'malformed handshake protocol: '
1010 1011 b'missing %s' % line)
1011 1012 ok = False
1012 1013 state = 'shutdown'
1013 1014 break
1014 1015
1015 1016 if not ok:
1016 1017 continue
1017 1018
1018 1019 request = fin.read(81)
1019 1020 if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
1020 1021 _sshv1respondooberror(fout, ui.ferr,
1021 1022 b'malformed handshake protocol: '
1022 1023 b'missing between argument value')
1023 1024 state = 'shutdown'
1024 1025 continue
1025 1026
1026 1027 state = 'upgrade-v2-finish'
1027 1028 continue
1028 1029
1029 1030 elif state == 'upgrade-v2-finish':
1030 1031 # Send the upgrade response.
1031 1032 fout.write(b'upgraded %s %s\n' % (token, SSHV2))
1032 1033 servercaps = wireproto.capabilities(repo, proto)
1033 1034 rsp = b'capabilities: %s' % servercaps.data
1034 1035 fout.write(b'%d\n%s\n' % (len(rsp), rsp))
1035 1036 fout.flush()
1036 1037
1037 1038 proto = sshv2protocolhandler(ui, fin, fout)
1038 1039 protoswitched = True
1039 1040
1040 1041 state = 'protov2-serving'
1041 1042 continue
1042 1043
1043 1044 elif state == 'shutdown':
1044 1045 break
1045 1046
1046 1047 else:
1047 1048 raise error.ProgrammingError('unhandled ssh server state: %s' %
1048 1049 state)
1049 1050
1050 1051 class sshserver(object):
1051 1052 def __init__(self, ui, repo, logfh=None):
1052 1053 self._ui = ui
1053 1054 self._repo = repo
1054 1055 self._fin = ui.fin
1055 1056 self._fout = ui.fout
1056 1057
1057 1058 # Log write I/O to stdout and stderr if configured.
1058 1059 if logfh:
1059 1060 self._fout = util.makeloggingfileobject(
1060 1061 logfh, self._fout, 'o', logdata=True)
1061 1062 ui.ferr = util.makeloggingfileobject(
1062 1063 logfh, ui.ferr, 'e', logdata=True)
1063 1064
1064 1065 hook.redirect(True)
1065 1066 ui.fout = repo.ui.fout = ui.ferr
1066 1067
1067 1068 # Prevent insertion/deletion of CRs
1068 1069 procutil.setbinary(self._fin)
1069 1070 procutil.setbinary(self._fout)
1070 1071
1071 1072 def serve_forever(self):
1072 1073 self.serveuntil(threading.Event())
1073 1074 sys.exit(0)
1074 1075
1075 1076 def serveuntil(self, ev):
1076 1077 """Serve until a threading.Event is set."""
1077 1078 _runsshserver(self._ui, self._repo, self._fin, self._fout, ev)
@@ -1,40 +1,40 b''
1 1 $ . $TESTDIR/wireprotohelpers.sh
2 2
3 3 $ hg init server
4 4 $ enablehttpv2 server
5 5 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
6 6 $ cat hg.pid > $DAEMON_PIDS
7 7
8 8 capabilities request returns an array of capability strings
9 9
10 10 $ sendhttpv2peer << EOF
11 11 > command capabilities
12 12 > EOF
13 13 creating http peer for wire protocol version 2
14 14 sending capabilities command
15 15 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
16 16 s> Accept-Encoding: identity\r\n
17 17 s> accept: application/mercurial-exp-framing-0003\r\n
18 18 s> content-type: application/mercurial-exp-framing-0003\r\n
19 19 s> content-length: 27\r\n
20 20 s> host: $LOCALIP:$HGPORT\r\n (glob)
21 21 s> user-agent: Mercurial debugwireproto\r\n
22 22 s> \r\n
23 23 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
24 24 s> makefile('rb', None)
25 25 s> HTTP/1.1 200 OK\r\n
26 26 s> Server: testing stub value\r\n
27 27 s> Date: $HTTP_DATE$\r\n
28 28 s> Content-Type: application/mercurial-exp-framing-0003\r\n
29 29 s> Transfer-Encoding: chunked\r\n
30 30 s> \r\n
31 31 s> *\r\n (glob)
32 32 s> *\x00\x01\x00\x02\x01F (glob)
33 s> \xa2Hcommands\xaaEheads\xa2Dargs\x81JpubliconlyKpermissions\x81DpullEknown\xa2Dargs\x81EnodesKpermissions\x81DpullFlookup\xa2Dargs\x81CkeyKpermissions\x81DpullGpushkey\xa2Dargs\x84CkeyInamespaceCnewColdKpermissions\x81DpushHlistkeys\xa2Dargs\x81InamespaceKpermissions\x81DpullHunbundle\xa2Dargs\x81EheadsKpermissions\x81DpushIbranchmap\xa2Dargs\x80Kpermissions\x81DpullIgetbundle\xa2Dargs\x81A*Kpermissions\x81DpullLcapabilities\xa2Dargs\x80Kpermissions\x81DpullLclonebundles\xa2Dargs\x80Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlib
33 s> \xa2Hcommands\xaaEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyFlegacyKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyFlegacyCnewFlegacyColdFlegacyInamespaceFlegacyKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullHunbundle\xa2Dargs\xa1EheadsFlegacyKpermissions\x81DpushIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullIgetbundle\xa2Dargs\xa1A*FlegacyKpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullLclonebundles\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x82\xa1DnameDzstd\xa1DnameDzlib
34 34 s> \r\n
35 35 received frame(size=*; request=1; stream=2; streamflags=stream-begin; type=bytes-response; flags=eos|cbor) (glob)
36 36 s> 0\r\n
37 37 s> \r\n
38 response: [{b'commands': {b'branchmap': {b'args': [], b'permissions': [b'pull']}, b'capabilities': {b'args': [], b'permissions': [b'pull']}, b'clonebundles': {b'args': [], b'permissions': [b'pull']}, b'getbundle': {b'args': [b'*'], b'permissions': [b'pull']}, b'heads': {b'args': [b'publiconly'], b'permissions': [b'pull']}, b'known': {b'args': [b'nodes'], b'permissions': [b'pull']}, b'listkeys': {b'args': [b'namespace'], b'permissions': [b'pull']}, b'lookup': {b'args': [b'key'], b'permissions': [b'pull']}, b'pushkey': {b'args': [b'key', b'namespace', b'new', b'old'], b'permissions': [b'push']}, b'unbundle': {b'args': [b'heads'], b'permissions': [b'push']}}, b'compression': [{b'name': b'zstd'}, {b'name': b'zlib'}]}]
38 response: [{b'commands': {b'branchmap': {b'args': {}, b'permissions': [b'pull']}, b'capabilities': {b'args': {}, b'permissions': [b'pull']}, b'clonebundles': {b'args': {}, b'permissions': [b'pull']}, b'getbundle': {b'args': {b'*': b'legacy'}, b'permissions': [b'pull']}, b'heads': {b'args': {b'publiconly': False}, b'permissions': [b'pull']}, b'known': {b'args': {b'nodes': [b'deadbeef']}, b'permissions': [b'pull']}, b'listkeys': {b'args': {b'namespace': b'ns'}, b'permissions': [b'pull']}, b'lookup': {b'args': {b'key': b'legacy'}, b'permissions': [b'pull']}, b'pushkey': {b'args': {b'key': b'legacy', b'namespace': b'legacy', b'new': b'legacy', b'old': b'legacy'}, b'permissions': [b'push']}, b'unbundle': {b'args': {b'heads': b'legacy'}, b'permissions': [b'push']}}, b'compression': [{b'name': b'zstd'}, {b'name': b'zlib'}]}]
39 39
40 40 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now