##// END OF EJS Templates
wireproto: service multiple command requests per HTTP request...
Gregory Szorc -
r37077:bbea9916 default
parent child Browse files
Show More
@@ -1,1390 +1,1412 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 The command to run is specified in the POST payload as defined by the
207 *Unified Frame-Based Protocol*. This is redundant with data already
208 encoded in the URL. This is by design, so server operators can have
209 better understanding about server activity from looking merely at
210 HTTP access logs.
211
212 In most circumstances, the command specified in the URL MUST match
213 the command specified in the frame-based payload or the server will
214 respond with an error. The exception to this is the special
215 ``multirequest`` URL. (See below.) In addition, HTTP requests
216 are limited to one command invocation. The exception is the special
217 ``multirequest`` URL.
218
219 The ``multirequest`` command endpoints (``ro/multirequest`` and
220 ``rw/multirequest``) are special in that they allow the execution of
221 *any* command and allow the execution of multiple commands. If the
222 HTTP request issues multiple commands across multiple frames, all
223 issued commands will be processed by the server. Per the defined
224 behavior of the *Unified Frame-Based Protocol*, commands may be
225 issued interleaved and responses may come back in a different order
226 than they were issued. Clients MUST be able to deal with this.
227
206 228 SSH Protocol
207 229 ============
208 230
209 231 Handshake
210 232 ---------
211 233
212 234 For all clients, the handshake consists of the client sending 1 or more
213 235 commands to the server using version 1 of the transport. Servers respond
214 236 to commands they know how to respond to and send an empty response (``0\n``)
215 237 for unknown commands (per standard behavior of version 1 of the transport).
216 238 Clients then typically look for a response to the newest sent command to
217 239 determine which transport version to use and what the available features for
218 240 the connection and server are.
219 241
220 242 Preceding any response from client-issued commands, the server may print
221 243 non-protocol output. It is common for SSH servers to print banners, message
222 244 of the day announcements, etc when clients connect. It is assumed that any
223 245 such *banner* output will precede any Mercurial server output. So clients
224 246 must be prepared to handle server output on initial connect that isn't
225 247 in response to any client-issued command and doesn't conform to Mercurial's
226 248 wire protocol. This *banner* output should only be on stdout. However,
227 249 some servers may send output on stderr.
228 250
229 251 Pre 0.9.1 clients issue a ``between`` command with the ``pairs`` argument
230 252 having the value
231 253 ``0000000000000000000000000000000000000000-0000000000000000000000000000000000000000``.
232 254
233 255 The ``between`` command has been supported since the original Mercurial
234 256 SSH server. Requesting the empty range will return a ``\n`` string response,
235 257 which will be encoded as ``1\n\n`` (value length of ``1`` followed by a newline
236 258 followed by the value, which happens to be a newline).
237 259
238 260 For pre 0.9.1 clients and all servers, the exchange looks like::
239 261
240 262 c: between\n
241 263 c: pairs 81\n
242 264 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
243 265 s: 1\n
244 266 s: \n
245 267
246 268 0.9.1+ clients send a ``hello`` command (with no arguments) before the
247 269 ``between`` command. The response to this command allows clients to
248 270 discover server capabilities and settings.
249 271
250 272 An example exchange between 0.9.1+ clients and a ``hello`` aware server looks
251 273 like::
252 274
253 275 c: hello\n
254 276 c: between\n
255 277 c: pairs 81\n
256 278 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
257 279 s: 324\n
258 280 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
259 281 s: 1\n
260 282 s: \n
261 283
262 284 And a similar scenario but with servers sending a banner on connect::
263 285
264 286 c: hello\n
265 287 c: between\n
266 288 c: pairs 81\n
267 289 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
268 290 s: welcome to the server\n
269 291 s: if you find any issues, email someone@somewhere.com\n
270 292 s: 324\n
271 293 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
272 294 s: 1\n
273 295 s: \n
274 296
275 297 Note that output from the ``hello`` command is terminated by a ``\n``. This is
276 298 part of the response payload and not part of the wire protocol adding a newline
277 299 after responses. In other words, the length of the response contains the
278 300 trailing ``\n``.
279 301
280 302 Clients supporting version 2 of the SSH transport send a line beginning
281 303 with ``upgrade`` before the ``hello`` and ``between`` commands. The line
282 304 (which isn't a well-formed command line because it doesn't consist of a
283 305 single command name) serves to both communicate the client's intent to
284 306 switch to transport version 2 (transports are version 1 by default) as
285 307 well as to advertise the client's transport-level capabilities so the
286 308 server may satisfy that request immediately.
287 309
288 310 The upgrade line has the form:
289 311
290 312 upgrade <token> <transport capabilities>
291 313
292 314 That is the literal string ``upgrade`` followed by a space, followed by
293 315 a randomly generated string, followed by a space, followed by a string
294 316 denoting the client's transport capabilities.
295 317
296 318 The token can be anything. However, a random UUID is recommended. (Use
297 319 of version 4 UUIDs is recommended because version 1 UUIDs can leak the
298 320 client's MAC address.)
299 321
300 322 The transport capabilities string is a URL/percent encoded string
301 323 containing key-value pairs defining the client's transport-level
302 324 capabilities. The following capabilities are defined:
303 325
304 326 proto
305 327 A comma-delimited list of transport protocol versions the client
306 328 supports. e.g. ``ssh-v2``.
307 329
308 330 If the server does not recognize the ``upgrade`` line, it should issue
309 331 an empty response and continue processing the ``hello`` and ``between``
310 332 commands. Here is an example handshake between a version 2 aware client
311 333 and a non version 2 aware server:
312 334
313 335 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
314 336 c: hello\n
315 337 c: between\n
316 338 c: pairs 81\n
317 339 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
318 340 s: 0\n
319 341 s: 324\n
320 342 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
321 343 s: 1\n
322 344 s: \n
323 345
324 346 (The initial ``0\n`` line from the server indicates an empty response to
325 347 the unknown ``upgrade ..`` command/line.)
326 348
327 349 If the server recognizes the ``upgrade`` line and is willing to satisfy that
328 350 upgrade request, it replies to with a payload of the following form:
329 351
330 352 upgraded <token> <transport name>\n
331 353
332 354 This line is the literal string ``upgraded``, a space, the token that was
333 355 specified by the client in its ``upgrade ...`` request line, a space, and the
334 356 name of the transport protocol that was chosen by the server. The transport
335 357 name MUST match one of the names the client specified in the ``proto`` field
336 358 of its ``upgrade ...`` request line.
337 359
338 360 If a server issues an ``upgraded`` response, it MUST also read and ignore
339 361 the lines associated with the ``hello`` and ``between`` command requests
340 362 that were issued by the server. It is assumed that the negotiated transport
341 363 will respond with equivalent requested information following the transport
342 364 handshake.
343 365
344 366 All data following the ``\n`` terminating the ``upgraded`` line is the
345 367 domain of the negotiated transport. It is common for the data immediately
346 368 following to contain additional metadata about the state of the transport and
347 369 the server. However, this isn't strictly speaking part of the transport
348 370 handshake and isn't covered by this section.
349 371
350 372 Here is an example handshake between a version 2 aware client and a version
351 373 2 aware server:
352 374
353 375 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
354 376 c: hello\n
355 377 c: between\n
356 378 c: pairs 81\n
357 379 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
358 380 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
359 381 s: <additional transport specific data>
360 382
361 383 The client-issued token that is echoed in the response provides a more
362 384 resilient mechanism for differentiating *banner* output from Mercurial
363 385 output. In version 1, properly formatted banner output could get confused
364 386 for Mercurial server output. By submitting a randomly generated token
365 387 that is then present in the response, the client can look for that token
366 388 in response lines and have reasonable certainty that the line did not
367 389 originate from a *banner* message.
368 390
369 391 SSH Version 1 Transport
370 392 -----------------------
371 393
372 394 The SSH transport (version 1) is a custom text-based protocol suitable for
373 395 use over any bi-directional stream transport. It is most commonly used with
374 396 SSH.
375 397
376 398 A SSH transport server can be started with ``hg serve --stdio``. The stdin,
377 399 stderr, and stdout file descriptors of the started process are used to exchange
378 400 data. When Mercurial connects to a remote server over SSH, it actually starts
379 401 a ``hg serve --stdio`` process on the remote server.
380 402
381 403 Commands are issued by sending the command name followed by a trailing newline
382 404 ``\n`` to the server. e.g. ``capabilities\n``.
383 405
384 406 Command arguments are sent in the following format::
385 407
386 408 <argument> <length>\n<value>
387 409
388 410 That is, the argument string name followed by a space followed by the
389 411 integer length of the value (expressed as a string) followed by a newline
390 412 (``\n``) followed by the raw argument value.
391 413
392 414 Dictionary arguments are encoded differently::
393 415
394 416 <argument> <# elements>\n
395 417 <key1> <length1>\n<value1>
396 418 <key2> <length2>\n<value2>
397 419 ...
398 420
399 421 Non-argument data is sent immediately after the final argument value. It is
400 422 encoded in chunks::
401 423
402 424 <length>\n<data>
403 425
404 426 Each command declares a list of supported arguments and their types. If a
405 427 client sends an unknown argument to the server, the server should abort
406 428 immediately. The special argument ``*`` in a command's definition indicates
407 429 that all argument names are allowed.
408 430
409 431 The definition of supported arguments and types is initially made when a
410 432 new command is implemented. The client and server must initially independently
411 433 agree on the arguments and their types. This initial set of arguments can be
412 434 supplemented through the presence of *capabilities* advertised by the server.
413 435
414 436 Each command has a defined expected response type.
415 437
416 438 A ``string`` response type is a length framed value. The response consists of
417 439 the string encoded integer length of a value followed by a newline (``\n``)
418 440 followed by the value. Empty values are allowed (and are represented as
419 441 ``0\n``).
420 442
421 443 A ``stream`` response type consists of raw bytes of data. There is no framing.
422 444
423 445 A generic error response type is also supported. It consists of a an error
424 446 message written to ``stderr`` followed by ``\n-\n``. In addition, ``\n`` is
425 447 written to ``stdout``.
426 448
427 449 If the server receives an unknown command, it will send an empty ``string``
428 450 response.
429 451
430 452 The server terminates if it receives an empty command (a ``\n`` character).
431 453
432 454 SSH Version 2 Transport
433 455 -----------------------
434 456
435 457 **Experimental and under development**
436 458
437 459 Version 2 of the SSH transport behaves identically to version 1 of the SSH
438 460 transport with the exception of handshake semantics. See above for how
439 461 version 2 of the SSH transport is negotiated.
440 462
441 463 Immediately following the ``upgraded`` line signaling a switch to version
442 464 2 of the SSH protocol, the server automatically sends additional details
443 465 about the capabilities of the remote server. This has the form:
444 466
445 467 <integer length of value>\n
446 468 capabilities: ...\n
447 469
448 470 e.g.
449 471
450 472 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
451 473 s: 240\n
452 474 s: capabilities: known getbundle batch ...\n
453 475
454 476 Following capabilities advertisement, the peers communicate using version
455 477 1 of the SSH transport.
456 478
457 479 Unified Frame-Based Protocol
458 480 ============================
459 481
460 482 **Experimental and under development**
461 483
462 484 The *Unified Frame-Based Protocol* is a communications protocol between
463 485 Mercurial peers. The protocol aims to be mostly transport agnostic
464 486 (works similarly on HTTP, SSH, etc).
465 487
466 488 To operate the protocol, a bi-directional, half-duplex pipe supporting
467 489 ordered sends and receives is required. That is, each peer has one pipe
468 490 for sending data and another for receiving.
469 491
470 492 The protocol is request-response based: the client issues requests to
471 493 the server, which issues replies to those requests. Server-initiated
472 494 messaging is not currently supported, but this specification carves
473 495 out room to implement it.
474 496
475 497 All data is read and written in atomic units called *frames*. These
476 498 are conceptually similar to TCP packets. Higher-level functionality
477 499 is built on the exchange and processing of frames.
478 500
479 501 All frames are associated with a numbered request. Frames can thus
480 502 be logically grouped by their request ID.
481 503
482 504 Frames begin with a 6 octet header followed by a variable length
483 505 payload::
484 506
485 507 +-----------------------------------------------+
486 508 | Length (24) |
487 509 +---------------------------------+-------------+
488 510 | Request ID (16) |
489 511 +----------+-----------+----------+
490 512 | Type (4) | Flags (4) |
491 513 +==========+===========+========================================|
492 514 | Frame Payload (0...) ...
493 515 +---------------------------------------------------------------+
494 516
495 517 The length of the frame payload is expressed as an unsigned 24 bit
496 518 little endian integer. Values larger than 65535 MUST NOT be used unless
497 519 given permission by the server as part of the negotiated capabilities
498 520 during the handshake. The frame header is not part of the advertised
499 521 frame length.
500 522
501 523 The 16-bit ``Request ID`` field denotes the integer request identifier,
502 524 stored as an unsigned little endian integer. Odd numbered requests are
503 525 client-initiated. Even numbered requests are server-initiated. This
504 526 refers to where the *request* was initiated - not where the *frame* was
505 527 initiated, so servers will send frames with odd ``Request ID`` in
506 528 response to client-initiated requests. Implementations are advised to
507 529 start ordering request identifiers at ``1`` and ``0``, increment by
508 530 ``2``, and wrap around if all available numbers have been exhausted.
509 531
510 532 The 4-bit ``Type`` field denotes the type of message being sent.
511 533
512 534 The 4-bit ``Flags`` field defines special, per-type attributes for
513 535 the frame.
514 536
515 537 The sections below define the frame types and their behavior.
516 538
517 539 Command Request (``0x01``)
518 540 --------------------------
519 541
520 542 This frame contains a request to run a command.
521 543
522 544 The name of the command to run constitutes the entirety of the frame
523 545 payload.
524 546
525 547 This frame type MUST ONLY be sent from clients to servers: it is illegal
526 548 for a server to send this frame to a client.
527 549
528 550 The following flag values are defined for this type:
529 551
530 552 0x01
531 553 End of command data. When set, the client will not send any command
532 554 arguments or additional command data. When set, the command has been
533 555 fully issued and the server has the full context to process the command.
534 556 The next frame issued by the client is not part of this command.
535 557 0x02
536 558 Command argument frames expected. When set, the client will send
537 559 *Command Argument* frames containing command argument data.
538 560 0x04
539 561 Command data frames expected. When set, the client will send
540 562 *Command Data* frames containing a raw stream of data for this
541 563 command.
542 564
543 565 The ``0x01`` flag is mutually exclusive with both the ``0x02`` and ``0x04``
544 566 flags.
545 567
546 568 Command Argument (``0x02``)
547 569 ---------------------------
548 570
549 571 This frame contains a named argument for a command.
550 572
551 573 The frame type MUST ONLY be sent from clients to servers: it is illegal
552 574 for a server to send this frame to a client.
553 575
554 576 The payload consists of:
555 577
556 578 * A 16-bit little endian integer denoting the length of the
557 579 argument name.
558 580 * A 16-bit little endian integer denoting the length of the
559 581 argument value.
560 582 * N bytes of ASCII data containing the argument name.
561 583 * N bytes of binary data containing the argument value.
562 584
563 585 The payload MUST hold the entirety of the 32-bit header and the
564 586 argument name. The argument value MAY span multiple frames. If this
565 587 occurs, the appropriate frame flag should be set to indicate this.
566 588
567 589 The following flag values are defined for this type:
568 590
569 591 0x01
570 592 Argument data continuation. When set, the data for this argument did
571 593 not fit in a single frame and the next frame will contain additional
572 594 argument data.
573 595
574 596 0x02
575 597 End of arguments data. When set, the client will not send any more
576 598 command arguments for the command this frame is associated with.
577 599 The next frame issued by the client will be command data or
578 600 belong to a separate request.
579 601
580 602 Command Data (``0x03``)
581 603 -----------------------
582 604
583 605 This frame contains raw data for a command.
584 606
585 607 Most commands can be executed by specifying arguments. However,
586 608 arguments have an upper bound to their length. For commands that
587 609 accept data that is beyond this length or whose length isn't known
588 610 when the command is initially sent, they will need to stream
589 611 arbitrary data to the server. This frame type facilitates the sending
590 612 of this data.
591 613
592 614 The payload of this frame type consists of a stream of raw data to be
593 615 consumed by the command handler on the server. The format of the data
594 616 is command specific.
595 617
596 618 The following flag values are defined for this type:
597 619
598 620 0x01
599 621 Command data continuation. When set, the data for this command
600 622 continues into a subsequent frame.
601 623
602 624 0x02
603 625 End of data. When set, command data has been fully sent to the
604 626 server. The command has been fully issued and no new data for this
605 627 command will be sent. The next frame will belong to a new command.
606 628
607 629 Bytes Response Data (``0x04``)
608 630 ------------------------------
609 631
610 632 This frame contains raw bytes response data to an issued command.
611 633
612 634 The following flag values are defined for this type:
613 635
614 636 0x01
615 637 Data continuation. When set, an additional frame containing raw
616 638 response data will follow.
617 639 0x02
618 640 End of data. When sent, the response data has been fully sent and
619 641 no additional frames for this response will be sent.
620 642
621 643 The ``0x01`` flag is mutually exclusive with the ``0x02`` flag.
622 644
623 645 Error Response (``0x05``)
624 646 -------------------------
625 647
626 648 An error occurred when processing a request. This could indicate
627 649 a protocol-level failure or an application level failure depending
628 650 on the flags for this message type.
629 651
630 652 The payload for this type is an error message that should be
631 653 displayed to the user.
632 654
633 655 The following flag values are defined for this type:
634 656
635 657 0x01
636 658 The error occurred at the transport/protocol level. If set, the
637 659 connection should be closed.
638 660 0x02
639 661 The error occurred at the application level. e.g. invalid command.
640 662
641 663 Issuing Commands
642 664 ----------------
643 665
644 666 A client can request that a remote run a command by sending it
645 667 frames defining that command. This logical stream is composed of
646 668 1 ``Command Request`` frame, 0 or more ``Command Argument`` frames,
647 669 and 0 or more ``Command Data`` frames.
648 670
649 671 All frames composing a single command request MUST be associated with
650 672 the same ``Request ID``.
651 673
652 674 Clients MAY send additional command requests without waiting on the
653 675 response to a previous command request. If they do so, they MUST ensure
654 676 that the ``Request ID`` field of outbound frames does not conflict
655 677 with that of an active ``Request ID`` whose response has not yet been
656 678 fully received.
657 679
658 680 Servers MAY respond to commands in a different order than they were
659 681 sent over the wire. Clients MUST be prepared to deal with this. Servers
660 682 also MAY start executing commands in a different order than they were
661 683 received, or MAY execute multiple commands concurrently.
662 684
663 685 If there is a dependency between commands or a race condition between
664 686 commands executing (e.g. a read-only command that depends on the results
665 687 of a command that mutates the repository), then clients MUST NOT send
666 688 frames issuing a command until a response to all dependent commands has
667 689 been received.
668 690 TODO think about whether we should express dependencies between commands
669 691 to avoid roundtrip latency.
670 692
671 693 Argument frames are the recommended mechanism for transferring fixed
672 694 sets of parameters to a command. Data frames are appropriate for
673 695 transferring variable data. A similar comparison would be to HTTP:
674 696 argument frames are headers and the message body is data frames.
675 697
676 698 It is recommended for servers to delay the dispatch of a command
677 699 until all argument frames for that command have been received. Servers
678 700 MAY impose limits on the maximum argument size.
679 701 TODO define failure mechanism.
680 702
681 703 Servers MAY dispatch to commands immediately once argument data
682 704 is available or delay until command data is received in full.
683 705
684 706 Capabilities
685 707 ============
686 708
687 709 Servers advertise supported wire protocol features. This allows clients to
688 710 probe for server features before blindly calling a command or passing a
689 711 specific argument.
690 712
691 713 The server's features are exposed via a *capabilities* string. This is a
692 714 space-delimited string of tokens/features. Some features are single words
693 715 like ``lookup`` or ``batch``. Others are complicated key-value pairs
694 716 advertising sub-features. e.g. ``httpheader=2048``. When complex, non-word
695 717 values are used, each feature name can define its own encoding of sub-values.
696 718 Comma-delimited and ``x-www-form-urlencoded`` values are common.
697 719
698 720 The following document capabilities defined by the canonical Mercurial server
699 721 implementation.
700 722
701 723 batch
702 724 -----
703 725
704 726 Whether the server supports the ``batch`` command.
705 727
706 728 This capability/command was introduced in Mercurial 1.9 (released July 2011).
707 729
708 730 branchmap
709 731 ---------
710 732
711 733 Whether the server supports the ``branchmap`` command.
712 734
713 735 This capability/command was introduced in Mercurial 1.3 (released July 2009).
714 736
715 737 bundle2-exp
716 738 -----------
717 739
718 740 Precursor to ``bundle2`` capability that was used before bundle2 was a
719 741 stable feature.
720 742
721 743 This capability was introduced in Mercurial 3.0 behind an experimental
722 744 flag. This capability should not be observed in the wild.
723 745
724 746 bundle2
725 747 -------
726 748
727 749 Indicates whether the server supports the ``bundle2`` data exchange format.
728 750
729 751 The value of the capability is a URL quoted, newline (``\n``) delimited
730 752 list of keys or key-value pairs.
731 753
732 754 A key is simply a URL encoded string.
733 755
734 756 A key-value pair is a URL encoded key separated from a URL encoded value by
735 757 an ``=``. If the value is a list, elements are delimited by a ``,`` after
736 758 URL encoding.
737 759
738 760 For example, say we have the values::
739 761
740 762 {'HG20': [], 'changegroup': ['01', '02'], 'digests': ['sha1', 'sha512']}
741 763
742 764 We would first construct a string::
743 765
744 766 HG20\nchangegroup=01,02\ndigests=sha1,sha512
745 767
746 768 We would then URL quote this string::
747 769
748 770 HG20%0Achangegroup%3D01%2C02%0Adigests%3Dsha1%2Csha512
749 771
750 772 This capability was introduced in Mercurial 3.4 (released May 2015).
751 773
752 774 changegroupsubset
753 775 -----------------
754 776
755 777 Whether the server supports the ``changegroupsubset`` command.
756 778
757 779 This capability was introduced in Mercurial 0.9.2 (released December
758 780 2006).
759 781
760 782 This capability was introduced at the same time as the ``lookup``
761 783 capability/command.
762 784
763 785 compression
764 786 -----------
765 787
766 788 Declares support for negotiating compression formats.
767 789
768 790 Presence of this capability indicates the server supports dynamic selection
769 791 of compression formats based on the client request.
770 792
771 793 Servers advertising this capability are required to support the
772 794 ``application/mercurial-0.2`` media type in response to commands returning
773 795 streams. Servers may support this media type on any command.
774 796
775 797 The value of the capability is a comma-delimited list of strings declaring
776 798 supported compression formats. The order of the compression formats is in
777 799 server-preferred order, most preferred first.
778 800
779 801 The identifiers used by the official Mercurial distribution are:
780 802
781 803 bzip2
782 804 bzip2
783 805 none
784 806 uncompressed / raw data
785 807 zlib
786 808 zlib (no gzip header)
787 809 zstd
788 810 zstd
789 811
790 812 This capability was introduced in Mercurial 4.1 (released February 2017).
791 813
792 814 getbundle
793 815 ---------
794 816
795 817 Whether the server supports the ``getbundle`` command.
796 818
797 819 This capability was introduced in Mercurial 1.9 (released July 2011).
798 820
799 821 httpheader
800 822 ----------
801 823
802 824 Whether the server supports receiving command arguments via HTTP request
803 825 headers.
804 826
805 827 The value of the capability is an integer describing the max header
806 828 length that clients should send. Clients should ignore any content after a
807 829 comma in the value, as this is reserved for future use.
808 830
809 831 This capability was introduced in Mercurial 1.9 (released July 2011).
810 832
811 833 httpmediatype
812 834 -------------
813 835
814 836 Indicates which HTTP media types (``Content-Type`` header) the server is
815 837 capable of receiving and sending.
816 838
817 839 The value of the capability is a comma-delimited list of strings identifying
818 840 support for media type and transmission direction. The following strings may
819 841 be present:
820 842
821 843 0.1rx
822 844 Indicates server support for receiving ``application/mercurial-0.1`` media
823 845 types.
824 846
825 847 0.1tx
826 848 Indicates server support for sending ``application/mercurial-0.1`` media
827 849 types.
828 850
829 851 0.2rx
830 852 Indicates server support for receiving ``application/mercurial-0.2`` media
831 853 types.
832 854
833 855 0.2tx
834 856 Indicates server support for sending ``application/mercurial-0.2`` media
835 857 types.
836 858
837 859 minrx=X
838 860 Minimum media type version the server is capable of receiving. Value is a
839 861 string like ``0.2``.
840 862
841 863 This capability can be used by servers to limit connections from legacy
842 864 clients not using the latest supported media type. However, only clients
843 865 with knowledge of this capability will know to consult this value. This
844 866 capability is present so the client may issue a more user-friendly error
845 867 when the server has locked out a legacy client.
846 868
847 869 mintx=X
848 870 Minimum media type version the server is capable of sending. Value is a
849 871 string like ``0.1``.
850 872
851 873 Servers advertising support for the ``application/mercurial-0.2`` media type
852 874 should also advertise the ``compression`` capability.
853 875
854 876 This capability was introduced in Mercurial 4.1 (released February 2017).
855 877
856 878 httppostargs
857 879 ------------
858 880
859 881 **Experimental**
860 882
861 883 Indicates that the server supports and prefers clients send command arguments
862 884 via a HTTP POST request as part of the request body.
863 885
864 886 This capability was introduced in Mercurial 3.8 (released May 2016).
865 887
866 888 known
867 889 -----
868 890
869 891 Whether the server supports the ``known`` command.
870 892
871 893 This capability/command was introduced in Mercurial 1.9 (released July 2011).
872 894
873 895 lookup
874 896 ------
875 897
876 898 Whether the server supports the ``lookup`` command.
877 899
878 900 This capability was introduced in Mercurial 0.9.2 (released December
879 901 2006).
880 902
881 903 This capability was introduced at the same time as the ``changegroupsubset``
882 904 capability/command.
883 905
884 906 pushkey
885 907 -------
886 908
887 909 Whether the server supports the ``pushkey`` and ``listkeys`` commands.
888 910
889 911 This capability was introduced in Mercurial 1.6 (released July 2010).
890 912
891 913 standardbundle
892 914 --------------
893 915
894 916 **Unsupported**
895 917
896 918 This capability was introduced during the Mercurial 0.9.2 development cycle in
897 919 2006. It was never present in a release, as it was replaced by the ``unbundle``
898 920 capability. This capability should not be encountered in the wild.
899 921
900 922 stream-preferred
901 923 ----------------
902 924
903 925 If present the server prefers that clients clone using the streaming clone
904 926 protocol (``hg clone --stream``) rather than the standard
905 927 changegroup/bundle based protocol.
906 928
907 929 This capability was introduced in Mercurial 2.2 (released May 2012).
908 930
909 931 streamreqs
910 932 ----------
911 933
912 934 Indicates whether the server supports *streaming clones* and the *requirements*
913 935 that clients must support to receive it.
914 936
915 937 If present, the server supports the ``stream_out`` command, which transmits
916 938 raw revlogs from the repository instead of changegroups. This provides a faster
917 939 cloning mechanism at the expense of more bandwidth used.
918 940
919 941 The value of this capability is a comma-delimited list of repo format
920 942 *requirements*. These are requirements that impact the reading of data in
921 943 the ``.hg/store`` directory. An example value is
922 944 ``streamreqs=generaldelta,revlogv1`` indicating the server repo requires
923 945 the ``revlogv1`` and ``generaldelta`` requirements.
924 946
925 947 If the only format requirement is ``revlogv1``, the server may expose the
926 948 ``stream`` capability instead of the ``streamreqs`` capability.
927 949
928 950 This capability was introduced in Mercurial 1.7 (released November 2010).
929 951
930 952 stream
931 953 ------
932 954
933 955 Whether the server supports *streaming clones* from ``revlogv1`` repos.
934 956
935 957 If present, the server supports the ``stream_out`` command, which transmits
936 958 raw revlogs from the repository instead of changegroups. This provides a faster
937 959 cloning mechanism at the expense of more bandwidth used.
938 960
939 961 This capability was introduced in Mercurial 0.9.1 (released July 2006).
940 962
941 963 When initially introduced, the value of the capability was the numeric
942 964 revlog revision. e.g. ``stream=1``. This indicates the changegroup is using
943 965 ``revlogv1``. This simple integer value wasn't powerful enough, so the
944 966 ``streamreqs`` capability was invented to handle cases where the repo
945 967 requirements have more than just ``revlogv1``. Newer servers omit the
946 968 ``=1`` since it was the only value supported and the value of ``1`` can
947 969 be implied by clients.
948 970
949 971 unbundlehash
950 972 ------------
951 973
952 974 Whether the ``unbundle`` commands supports receiving a hash of all the
953 975 heads instead of a list.
954 976
955 977 For more, see the documentation for the ``unbundle`` command.
956 978
957 979 This capability was introduced in Mercurial 1.9 (released July 2011).
958 980
959 981 unbundle
960 982 --------
961 983
962 984 Whether the server supports pushing via the ``unbundle`` command.
963 985
964 986 This capability/command has been present since Mercurial 0.9.1 (released
965 987 July 2006).
966 988
967 989 Mercurial 0.9.2 (released December 2006) added values to the capability
968 990 indicating which bundle types the server supports receiving. This value is a
969 991 comma-delimited list. e.g. ``HG10GZ,HG10BZ,HG10UN``. The order of values
970 992 reflects the priority/preference of that type, where the first value is the
971 993 most preferred type.
972 994
973 995 Content Negotiation
974 996 ===================
975 997
976 998 The wire protocol has some mechanisms to help peers determine what content
977 999 types and encoding the other side will accept. Historically, these mechanisms
978 1000 have been built into commands themselves because most commands only send a
979 1001 well-defined response type and only certain commands needed to support
980 1002 functionality like compression.
981 1003
982 1004 Currently, only the HTTP version 1 transport supports content negotiation
983 1005 at the protocol layer.
984 1006
985 1007 HTTP requests advertise supported response formats via the ``X-HgProto-<N>``
986 1008 request header, where ``<N>`` is an integer starting at 1 allowing the logical
987 1009 value to span multiple headers. This value consists of a list of
988 1010 space-delimited parameters. Each parameter denotes a feature or capability.
989 1011
990 1012 The following parameters are defined:
991 1013
992 1014 0.1
993 1015 Indicates the client supports receiving ``application/mercurial-0.1``
994 1016 responses.
995 1017
996 1018 0.2
997 1019 Indicates the client supports receiving ``application/mercurial-0.2``
998 1020 responses.
999 1021
1000 1022 comp
1001 1023 Indicates compression formats the client can decode. Value is a list of
1002 1024 comma delimited strings identifying compression formats ordered from
1003 1025 most preferential to least preferential. e.g. ``comp=zstd,zlib,none``.
1004 1026
1005 1027 This parameter does not have an effect if only the ``0.1`` parameter
1006 1028 is defined, as support for ``application/mercurial-0.2`` or greater is
1007 1029 required to use arbitrary compression formats.
1008 1030
1009 1031 If this parameter is not advertised, the server interprets this as
1010 1032 equivalent to ``zlib,none``.
1011 1033
1012 1034 Clients may choose to only send this header if the ``httpmediatype``
1013 1035 server capability is present, as currently all server-side features
1014 1036 consulting this header require the client to opt in to new protocol features
1015 1037 advertised via the ``httpmediatype`` capability.
1016 1038
1017 1039 A server that doesn't receive an ``X-HgProto-<N>`` header should infer a
1018 1040 value of ``0.1``. This is compatible with legacy clients.
1019 1041
1020 1042 A server receiving a request indicating support for multiple media type
1021 1043 versions may respond with any of the supported media types. Not all servers
1022 1044 may support all media types on all commands.
1023 1045
1024 1046 Commands
1025 1047 ========
1026 1048
1027 1049 This section contains a list of all wire protocol commands implemented by
1028 1050 the canonical Mercurial server.
1029 1051
1030 1052 batch
1031 1053 -----
1032 1054
1033 1055 Issue multiple commands while sending a single command request. The purpose
1034 1056 of this command is to allow a client to issue multiple commands while avoiding
1035 1057 multiple round trips to the server therefore enabling commands to complete
1036 1058 quicker.
1037 1059
1038 1060 The command accepts a ``cmds`` argument that contains a list of commands to
1039 1061 execute.
1040 1062
1041 1063 The value of ``cmds`` is a ``;`` delimited list of strings. Each string has the
1042 1064 form ``<command> <arguments>``. That is, the command name followed by a space
1043 1065 followed by an argument string.
1044 1066
1045 1067 The argument string is a ``,`` delimited list of ``<key>=<value>`` values
1046 1068 corresponding to command arguments. Both the argument name and value are
1047 1069 escaped using a special substitution map::
1048 1070
1049 1071 : -> :c
1050 1072 , -> :o
1051 1073 ; -> :s
1052 1074 = -> :e
1053 1075
1054 1076 The response type for this command is ``string``. The value contains a
1055 1077 ``;`` delimited list of responses for each requested command. Each value
1056 1078 in this list is escaped using the same substitution map used for arguments.
1057 1079
1058 1080 If an error occurs, the generic error response may be sent.
1059 1081
1060 1082 between
1061 1083 -------
1062 1084
1063 1085 (Legacy command used for discovery in old clients)
1064 1086
1065 1087 Obtain nodes between pairs of nodes.
1066 1088
1067 1089 The ``pairs`` arguments contains a space-delimited list of ``-`` delimited
1068 1090 hex node pairs. e.g.::
1069 1091
1070 1092 a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896-6dc58916e7c070f678682bfe404d2e2d68291a18
1071 1093
1072 1094 Return type is a ``string``. Value consists of lines corresponding to each
1073 1095 requested range. Each line contains a space-delimited list of hex nodes.
1074 1096 A newline ``\n`` terminates each line, including the last one.
1075 1097
1076 1098 branchmap
1077 1099 ---------
1078 1100
1079 1101 Obtain heads in named branches.
1080 1102
1081 1103 Accepts no arguments. Return type is a ``string``.
1082 1104
1083 1105 Return value contains lines with URL encoded branch names followed by a space
1084 1106 followed by a space-delimited list of hex nodes of heads on that branch.
1085 1107 e.g.::
1086 1108
1087 1109 default a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896 6dc58916e7c070f678682bfe404d2e2d68291a18
1088 1110 stable baae3bf31522f41dd5e6d7377d0edd8d1cf3fccc
1089 1111
1090 1112 There is no trailing newline.
1091 1113
1092 1114 branches
1093 1115 --------
1094 1116
1095 1117 (Legacy command used for discovery in old clients. Clients with ``getbundle``
1096 1118 use the ``known`` and ``heads`` commands instead.)
1097 1119
1098 1120 Obtain ancestor changesets of specific nodes back to a branch point.
1099 1121
1100 1122 Despite the name, this command has nothing to do with Mercurial named branches.
1101 1123 Instead, it is related to DAG branches.
1102 1124
1103 1125 The command accepts a ``nodes`` argument, which is a string of space-delimited
1104 1126 hex nodes.
1105 1127
1106 1128 For each node requested, the server will find the first ancestor node that is
1107 1129 a DAG root or is a merge.
1108 1130
1109 1131 Return type is a ``string``. Return value contains lines with result data for
1110 1132 each requested node. Each line contains space-delimited nodes followed by a
1111 1133 newline (``\n``). The 4 nodes reported on each line correspond to the requested
1112 1134 node, the ancestor node found, and its 2 parent nodes (which may be the null
1113 1135 node).
1114 1136
1115 1137 capabilities
1116 1138 ------------
1117 1139
1118 1140 Obtain the capabilities string for the repo.
1119 1141
1120 1142 Unlike the ``hello`` command, the capabilities string is not prefixed.
1121 1143 There is no trailing newline.
1122 1144
1123 1145 This command does not accept any arguments. Return type is a ``string``.
1124 1146
1125 1147 This command was introduced in Mercurial 0.9.1 (released July 2006).
1126 1148
1127 1149 changegroup
1128 1150 -----------
1129 1151
1130 1152 (Legacy command: use ``getbundle`` instead)
1131 1153
1132 1154 Obtain a changegroup version 1 with data for changesets that are
1133 1155 descendants of client-specified changesets.
1134 1156
1135 1157 The ``roots`` arguments contains a list of space-delimited hex nodes.
1136 1158
1137 1159 The server responds with a changegroup version 1 containing all
1138 1160 changesets between the requested root/base nodes and the repo's head nodes
1139 1161 at the time of the request.
1140 1162
1141 1163 The return type is a ``stream``.
1142 1164
1143 1165 changegroupsubset
1144 1166 -----------------
1145 1167
1146 1168 (Legacy command: use ``getbundle`` instead)
1147 1169
1148 1170 Obtain a changegroup version 1 with data for changesetsets between
1149 1171 client specified base and head nodes.
1150 1172
1151 1173 The ``bases`` argument contains a list of space-delimited hex nodes.
1152 1174 The ``heads`` argument contains a list of space-delimited hex nodes.
1153 1175
1154 1176 The server responds with a changegroup version 1 containing all
1155 1177 changesets between the requested base and head nodes at the time of the
1156 1178 request.
1157 1179
1158 1180 The return type is a ``stream``.
1159 1181
1160 1182 clonebundles
1161 1183 ------------
1162 1184
1163 1185 Obtains a manifest of bundle URLs available to seed clones.
1164 1186
1165 1187 Each returned line contains a URL followed by metadata. See the
1166 1188 documentation in the ``clonebundles`` extension for more.
1167 1189
1168 1190 The return type is a ``string``.
1169 1191
1170 1192 getbundle
1171 1193 ---------
1172 1194
1173 1195 Obtain a bundle containing repository data.
1174 1196
1175 1197 This command accepts the following arguments:
1176 1198
1177 1199 heads
1178 1200 List of space-delimited hex nodes of heads to retrieve.
1179 1201 common
1180 1202 List of space-delimited hex nodes that the client has in common with the
1181 1203 server.
1182 1204 obsmarkers
1183 1205 Boolean indicating whether to include obsolescence markers as part
1184 1206 of the response. Only works with bundle2.
1185 1207 bundlecaps
1186 1208 Comma-delimited set of strings defining client bundle capabilities.
1187 1209 listkeys
1188 1210 Comma-delimited list of strings of ``pushkey`` namespaces. For each
1189 1211 namespace listed, a bundle2 part will be included with the content of
1190 1212 that namespace.
1191 1213 cg
1192 1214 Boolean indicating whether changegroup data is requested.
1193 1215 cbattempted
1194 1216 Boolean indicating whether the client attempted to use the *clone bundles*
1195 1217 feature before performing this request.
1196 1218 bookmarks
1197 1219 Boolean indicating whether bookmark data is requested.
1198 1220 phases
1199 1221 Boolean indicating whether phases data is requested.
1200 1222
1201 1223 The return type on success is a ``stream`` where the value is bundle.
1202 1224 On the HTTP version 1 transport, the response is zlib compressed.
1203 1225
1204 1226 If an error occurs, a generic error response can be sent.
1205 1227
1206 1228 Unless the client sends a false value for the ``cg`` argument, the returned
1207 1229 bundle contains a changegroup with the nodes between the specified ``common``
1208 1230 and ``heads`` nodes. Depending on the command arguments, the type and content
1209 1231 of the returned bundle can vary significantly.
1210 1232
1211 1233 The default behavior is for the server to send a raw changegroup version
1212 1234 ``01`` response.
1213 1235
1214 1236 If the ``bundlecaps`` provided by the client contain a value beginning
1215 1237 with ``HG2``, a bundle2 will be returned. The bundle2 data may contain
1216 1238 additional repository data, such as ``pushkey`` namespace values.
1217 1239
1218 1240 heads
1219 1241 -----
1220 1242
1221 1243 Returns a list of space-delimited hex nodes of repository heads followed
1222 1244 by a newline. e.g.
1223 1245 ``a9eeb3adc7ddb5006c088e9eda61791c777cbf7c 31f91a3da534dc849f0d6bfc00a395a97cf218a1\n``
1224 1246
1225 1247 This command does not accept any arguments. The return type is a ``string``.
1226 1248
1227 1249 hello
1228 1250 -----
1229 1251
1230 1252 Returns lines describing interesting things about the server in an RFC-822
1231 1253 like format.
1232 1254
1233 1255 Currently, the only line defines the server capabilities. It has the form::
1234 1256
1235 1257 capabilities: <value>
1236 1258
1237 1259 See above for more about the capabilities string.
1238 1260
1239 1261 SSH clients typically issue this command as soon as a connection is
1240 1262 established.
1241 1263
1242 1264 This command does not accept any arguments. The return type is a ``string``.
1243 1265
1244 1266 This command was introduced in Mercurial 0.9.1 (released July 2006).
1245 1267
1246 1268 listkeys
1247 1269 --------
1248 1270
1249 1271 List values in a specified ``pushkey`` namespace.
1250 1272
1251 1273 The ``namespace`` argument defines the pushkey namespace to operate on.
1252 1274
1253 1275 The return type is a ``string``. The value is an encoded dictionary of keys.
1254 1276
1255 1277 Key-value pairs are delimited by newlines (``\n``). Within each line, keys and
1256 1278 values are separated by a tab (``\t``). Keys and values are both strings.
1257 1279
1258 1280 lookup
1259 1281 ------
1260 1282
1261 1283 Try to resolve a value to a known repository revision.
1262 1284
1263 1285 The ``key`` argument is converted from bytes to an
1264 1286 ``encoding.localstr`` instance then passed into
1265 1287 ``localrepository.__getitem__`` in an attempt to resolve it.
1266 1288
1267 1289 The return type is a ``string``.
1268 1290
1269 1291 Upon successful resolution, returns ``1 <hex node>\n``. On failure,
1270 1292 returns ``0 <error string>\n``. e.g.::
1271 1293
1272 1294 1 273ce12ad8f155317b2c078ec75a4eba507f1fba\n
1273 1295
1274 1296 0 unknown revision 'foo'\n
1275 1297
1276 1298 known
1277 1299 -----
1278 1300
1279 1301 Determine whether multiple nodes are known.
1280 1302
1281 1303 The ``nodes`` argument is a list of space-delimited hex nodes to check
1282 1304 for existence.
1283 1305
1284 1306 The return type is ``string``.
1285 1307
1286 1308 Returns a string consisting of ``0``s and ``1``s indicating whether nodes
1287 1309 are known. If the Nth node specified in the ``nodes`` argument is known,
1288 1310 a ``1`` will be returned at byte offset N. If the node isn't known, ``0``
1289 1311 will be present at byte offset N.
1290 1312
1291 1313 There is no trailing newline.
1292 1314
1293 1315 pushkey
1294 1316 -------
1295 1317
1296 1318 Set a value using the ``pushkey`` protocol.
1297 1319
1298 1320 Accepts arguments ``namespace``, ``key``, ``old``, and ``new``, which
1299 1321 correspond to the pushkey namespace to operate on, the key within that
1300 1322 namespace to change, the old value (which may be empty), and the new value.
1301 1323 All arguments are string types.
1302 1324
1303 1325 The return type is a ``string``. The value depends on the transport protocol.
1304 1326
1305 1327 The SSH version 1 transport sends a string encoded integer followed by a
1306 1328 newline (``\n``) which indicates operation result. The server may send
1307 1329 additional output on the ``stderr`` stream that should be displayed to the
1308 1330 user.
1309 1331
1310 1332 The HTTP version 1 transport sends a string encoded integer followed by a
1311 1333 newline followed by additional server output that should be displayed to
1312 1334 the user. This may include output from hooks, etc.
1313 1335
1314 1336 The integer result varies by namespace. ``0`` means an error has occurred
1315 1337 and there should be additional output to display to the user.
1316 1338
1317 1339 stream_out
1318 1340 ----------
1319 1341
1320 1342 Obtain *streaming clone* data.
1321 1343
1322 1344 The return type is either a ``string`` or a ``stream``, depending on
1323 1345 whether the request was fulfilled properly.
1324 1346
1325 1347 A return value of ``1\n`` indicates the server is not configured to serve
1326 1348 this data. If this is seen by the client, they may not have verified the
1327 1349 ``stream`` capability is set before making the request.
1328 1350
1329 1351 A return value of ``2\n`` indicates the server was unable to lock the
1330 1352 repository to generate data.
1331 1353
1332 1354 All other responses are a ``stream`` of bytes. The first line of this data
1333 1355 contains 2 space-delimited integers corresponding to the path count and
1334 1356 payload size, respectively::
1335 1357
1336 1358 <path count> <payload size>\n
1337 1359
1338 1360 The ``<payload size>`` is the total size of path data: it does not include
1339 1361 the size of the per-path header lines.
1340 1362
1341 1363 Following that header are ``<path count>`` entries. Each entry consists of a
1342 1364 line with metadata followed by raw revlog data. The line consists of::
1343 1365
1344 1366 <store path>\0<size>\n
1345 1367
1346 1368 The ``<store path>`` is the encoded store path of the data that follows.
1347 1369 ``<size>`` is the amount of data for this store path/revlog that follows the
1348 1370 newline.
1349 1371
1350 1372 There is no trailer to indicate end of data. Instead, the client should stop
1351 1373 reading after ``<path count>`` entries are consumed.
1352 1374
1353 1375 unbundle
1354 1376 --------
1355 1377
1356 1378 Send a bundle containing data (usually changegroup data) to the server.
1357 1379
1358 1380 Accepts the argument ``heads``, which is a space-delimited list of hex nodes
1359 1381 corresponding to server repository heads observed by the client. This is used
1360 1382 to detect race conditions and abort push operations before a server performs
1361 1383 too much work or a client transfers too much data.
1362 1384
1363 1385 The request payload consists of a bundle to be applied to the repository,
1364 1386 similarly to as if :hg:`unbundle` were called.
1365 1387
1366 1388 In most scenarios, a special ``push response`` type is returned. This type
1367 1389 contains an integer describing the change in heads as a result of the
1368 1390 operation. A value of ``0`` indicates nothing changed. ``1`` means the number
1369 1391 of heads remained the same. Values ``2`` and larger indicate the number of
1370 1392 added heads minus 1. e.g. ``3`` means 2 heads were added. Negative values
1371 1393 indicate the number of fewer heads, also off by 1. e.g. ``-2`` means there
1372 1394 is 1 fewer head.
1373 1395
1374 1396 The encoding of the ``push response`` type varies by transport.
1375 1397
1376 1398 For the SSH version 1 transport, this type is composed of 2 ``string``
1377 1399 responses: an empty response (``0\n``) followed by the integer result value.
1378 1400 e.g. ``1\n2``. So the full response might be ``0\n1\n2``.
1379 1401
1380 1402 For the HTTP version 1 transport, the response is a ``string`` type composed
1381 1403 of an integer result value followed by a newline (``\n``) followed by string
1382 1404 content holding server output that should be displayed on the client (output
1383 1405 hooks, etc).
1384 1406
1385 1407 In some cases, the server may respond with a ``bundle2`` bundle. In this
1386 1408 case, the response type is ``stream``. For the HTTP version 1 transport, the
1387 1409 response is zlib compressed.
1388 1410
1389 1411 The server may also respond with a generic error type, which contains a string
1390 1412 indicating the failure.
@@ -1,1017 +1,1043 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 . import (
16 16 encoding,
17 17 error,
18 18 hook,
19 19 pycompat,
20 20 util,
21 21 wireproto,
22 22 wireprotoframing,
23 23 wireprototypes,
24 24 )
25 25
26 26 stringio = util.stringio
27 27
28 28 urlerr = util.urlerr
29 29 urlreq = util.urlreq
30 30
31 31 HTTP_OK = 200
32 32
33 33 HGTYPE = 'application/mercurial-0.1'
34 34 HGTYPE2 = 'application/mercurial-0.2'
35 35 HGERRTYPE = 'application/hg-error'
36 36 FRAMINGTYPE = b'application/mercurial-exp-framing-0002'
37 37
38 38 HTTPV2 = wireprototypes.HTTPV2
39 39 SSHV1 = wireprototypes.SSHV1
40 40 SSHV2 = wireprototypes.SSHV2
41 41
42 42 def decodevaluefromheaders(req, headerprefix):
43 43 """Decode a long value from multiple HTTP request headers.
44 44
45 45 Returns the value as a bytes, not a str.
46 46 """
47 47 chunks = []
48 48 i = 1
49 49 while True:
50 50 v = req.headers.get(b'%s-%d' % (headerprefix, i))
51 51 if v is None:
52 52 break
53 53 chunks.append(pycompat.bytesurl(v))
54 54 i += 1
55 55
56 56 return ''.join(chunks)
57 57
58 58 class httpv1protocolhandler(wireprototypes.baseprotocolhandler):
59 59 def __init__(self, req, ui, checkperm):
60 60 self._req = req
61 61 self._ui = ui
62 62 self._checkperm = checkperm
63 63
64 64 @property
65 65 def name(self):
66 66 return 'http-v1'
67 67
68 68 def getargs(self, args):
69 69 knownargs = self._args()
70 70 data = {}
71 71 keys = args.split()
72 72 for k in keys:
73 73 if k == '*':
74 74 star = {}
75 75 for key in knownargs.keys():
76 76 if key != 'cmd' and key not in keys:
77 77 star[key] = knownargs[key][0]
78 78 data['*'] = star
79 79 else:
80 80 data[k] = knownargs[k][0]
81 81 return [data[k] for k in keys]
82 82
83 83 def _args(self):
84 84 args = self._req.qsparams.asdictoflists()
85 85 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
86 86 if postlen:
87 87 args.update(urlreq.parseqs(
88 88 self._req.bodyfh.read(postlen), keep_blank_values=True))
89 89 return args
90 90
91 91 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
92 92 args.update(urlreq.parseqs(argvalue, keep_blank_values=True))
93 93 return args
94 94
95 95 def forwardpayload(self, fp):
96 96 # Existing clients *always* send Content-Length.
97 97 length = int(self._req.headers[b'Content-Length'])
98 98
99 99 # If httppostargs is used, we need to read Content-Length
100 100 # minus the amount that was consumed by args.
101 101 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
102 102 for s in util.filechunkiter(self._req.bodyfh, limit=length):
103 103 fp.write(s)
104 104
105 105 @contextlib.contextmanager
106 106 def mayberedirectstdio(self):
107 107 oldout = self._ui.fout
108 108 olderr = self._ui.ferr
109 109
110 110 out = util.stringio()
111 111
112 112 try:
113 113 self._ui.fout = out
114 114 self._ui.ferr = out
115 115 yield out
116 116 finally:
117 117 self._ui.fout = oldout
118 118 self._ui.ferr = olderr
119 119
120 120 def client(self):
121 121 return 'remote:%s:%s:%s' % (
122 122 self._req.urlscheme,
123 123 urlreq.quote(self._req.remotehost or ''),
124 124 urlreq.quote(self._req.remoteuser or ''))
125 125
126 126 def addcapabilities(self, repo, caps):
127 127 caps.append(b'batch')
128 128
129 129 caps.append('httpheader=%d' %
130 130 repo.ui.configint('server', 'maxhttpheaderlen'))
131 131 if repo.ui.configbool('experimental', 'httppostargs'):
132 132 caps.append('httppostargs')
133 133
134 134 # FUTURE advertise 0.2rx once support is implemented
135 135 # FUTURE advertise minrx and mintx after consulting config option
136 136 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
137 137
138 138 compengines = wireproto.supportedcompengines(repo.ui, util.SERVERROLE)
139 139 if compengines:
140 140 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
141 141 for e in compengines)
142 142 caps.append('compression=%s' % comptypes)
143 143
144 144 return caps
145 145
146 146 def checkperm(self, perm):
147 147 return self._checkperm(perm)
148 148
149 149 # This method exists mostly so that extensions like remotefilelog can
150 150 # disable a kludgey legacy method only over http. As of early 2018,
151 151 # there are no other known users, so with any luck we can discard this
152 152 # hook if remotefilelog becomes a first-party extension.
153 153 def iscmd(cmd):
154 154 return cmd in wireproto.commands
155 155
156 156 def handlewsgirequest(rctx, req, res, checkperm):
157 157 """Possibly process a wire protocol request.
158 158
159 159 If the current request is a wire protocol request, the request is
160 160 processed by this function.
161 161
162 162 ``req`` is a ``parsedrequest`` instance.
163 163 ``res`` is a ``wsgiresponse`` instance.
164 164
165 165 Returns a bool indicating if the request was serviced. If set, the caller
166 166 should stop processing the request, as a response has already been issued.
167 167 """
168 168 # Avoid cycle involving hg module.
169 169 from .hgweb import common as hgwebcommon
170 170
171 171 repo = rctx.repo
172 172
173 173 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
174 174 # string parameter. If it isn't present, this isn't a wire protocol
175 175 # request.
176 176 if 'cmd' not in req.qsparams:
177 177 return False
178 178
179 179 cmd = req.qsparams['cmd']
180 180
181 181 # The "cmd" request parameter is used by both the wire protocol and hgweb.
182 182 # While not all wire protocol commands are available for all transports,
183 183 # if we see a "cmd" value that resembles a known wire protocol command, we
184 184 # route it to a protocol handler. This is better than routing possible
185 185 # wire protocol requests to hgweb because it prevents hgweb from using
186 186 # known wire protocol commands and it is less confusing for machine
187 187 # clients.
188 188 if not iscmd(cmd):
189 189 return False
190 190
191 191 # The "cmd" query string argument is only valid on the root path of the
192 192 # repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
193 193 # like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
194 194 # in this case. We send an HTTP 404 for backwards compatibility reasons.
195 195 if req.dispatchpath:
196 196 res.status = hgwebcommon.statusmessage(404)
197 197 res.headers['Content-Type'] = HGTYPE
198 198 # TODO This is not a good response to issue for this request. This
199 199 # is mostly for BC for now.
200 200 res.setbodybytes('0\n%s\n' % b'Not Found')
201 201 return True
202 202
203 203 proto = httpv1protocolhandler(req, repo.ui,
204 204 lambda perm: checkperm(rctx, req, perm))
205 205
206 206 # The permissions checker should be the only thing that can raise an
207 207 # ErrorResponse. It is kind of a layer violation to catch an hgweb
208 208 # exception here. So consider refactoring into a exception type that
209 209 # is associated with the wire protocol.
210 210 try:
211 211 _callhttp(repo, req, res, proto, cmd)
212 212 except hgwebcommon.ErrorResponse as e:
213 213 for k, v in e.headers:
214 214 res.headers[k] = v
215 215 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
216 216 # TODO This response body assumes the failed command was
217 217 # "unbundle." That assumption is not always valid.
218 218 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
219 219
220 220 return True
221 221
222 222 def handlewsgiapirequest(rctx, req, res, checkperm):
223 223 """Handle requests to /api/*."""
224 224 assert req.dispatchparts[0] == b'api'
225 225
226 226 repo = rctx.repo
227 227
228 228 # This whole URL space is experimental for now. But we want to
229 229 # reserve the URL space. So, 404 all URLs if the feature isn't enabled.
230 230 if not repo.ui.configbool('experimental', 'web.apiserver'):
231 231 res.status = b'404 Not Found'
232 232 res.headers[b'Content-Type'] = b'text/plain'
233 233 res.setbodybytes(_('Experimental API server endpoint not enabled'))
234 234 return
235 235
236 236 # The URL space is /api/<protocol>/*. The structure of URLs under varies
237 237 # by <protocol>.
238 238
239 239 # Registered APIs are made available via config options of the name of
240 240 # the protocol.
241 241 availableapis = set()
242 242 for k, v in API_HANDLERS.items():
243 243 section, option = v['config']
244 244 if repo.ui.configbool(section, option):
245 245 availableapis.add(k)
246 246
247 247 # Requests to /api/ list available APIs.
248 248 if req.dispatchparts == [b'api']:
249 249 res.status = b'200 OK'
250 250 res.headers[b'Content-Type'] = b'text/plain'
251 251 lines = [_('APIs can be accessed at /api/<name>, where <name> can be '
252 252 'one of the following:\n')]
253 253 if availableapis:
254 254 lines.extend(sorted(availableapis))
255 255 else:
256 256 lines.append(_('(no available APIs)\n'))
257 257 res.setbodybytes(b'\n'.join(lines))
258 258 return
259 259
260 260 proto = req.dispatchparts[1]
261 261
262 262 if proto not in API_HANDLERS:
263 263 res.status = b'404 Not Found'
264 264 res.headers[b'Content-Type'] = b'text/plain'
265 265 res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % (
266 266 proto, b', '.join(sorted(availableapis))))
267 267 return
268 268
269 269 if proto not in availableapis:
270 270 res.status = b'404 Not Found'
271 271 res.headers[b'Content-Type'] = b'text/plain'
272 272 res.setbodybytes(_('API %s not enabled\n') % proto)
273 273 return
274 274
275 275 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm,
276 276 req.dispatchparts[2:])
277 277
278 278 def _handlehttpv2request(rctx, req, res, checkperm, urlparts):
279 279 from .hgweb import common as hgwebcommon
280 280
281 281 # URL space looks like: <permissions>/<command>, where <permission> can
282 282 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
283 283
284 284 # Root URL does nothing meaningful... yet.
285 285 if not urlparts:
286 286 res.status = b'200 OK'
287 287 res.headers[b'Content-Type'] = b'text/plain'
288 288 res.setbodybytes(_('HTTP version 2 API handler'))
289 289 return
290 290
291 291 if len(urlparts) == 1:
292 292 res.status = b'404 Not Found'
293 293 res.headers[b'Content-Type'] = b'text/plain'
294 294 res.setbodybytes(_('do not know how to process %s\n') %
295 295 req.dispatchpath)
296 296 return
297 297
298 298 permission, command = urlparts[0:2]
299 299
300 300 if permission not in (b'ro', b'rw'):
301 301 res.status = b'404 Not Found'
302 302 res.headers[b'Content-Type'] = b'text/plain'
303 303 res.setbodybytes(_('unknown permission: %s') % permission)
304 304 return
305 305
306 306 if req.method != 'POST':
307 307 res.status = b'405 Method Not Allowed'
308 308 res.headers[b'Allow'] = b'POST'
309 309 res.setbodybytes(_('commands require POST requests'))
310 310 return
311 311
312 312 # At some point we'll want to use our own API instead of recycling the
313 313 # behavior of version 1 of the wire protocol...
314 314 # TODO return reasonable responses - not responses that overload the
315 315 # HTTP status line message for error reporting.
316 316 try:
317 317 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
318 318 except hgwebcommon.ErrorResponse as e:
319 319 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
320 320 for k, v in e.headers:
321 321 res.headers[k] = v
322 322 res.setbodybytes('permission denied')
323 323 return
324 324
325 325 # We have a special endpoint to reflect the request back at the client.
326 326 if command == b'debugreflect':
327 327 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
328 328 return
329 329
330 if command not in wireproto.commands:
330 # Extra commands that we handle that aren't really wire protocol
331 # commands. Think extra hard before making this hackery available to
332 # extension.
333 extracommands = {'multirequest'}
334
335 if command not in wireproto.commands and command not in extracommands:
331 336 res.status = b'404 Not Found'
332 337 res.headers[b'Content-Type'] = b'text/plain'
333 338 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
334 339 return
335 340
336 341 repo = rctx.repo
337 342 ui = repo.ui
338 343
339 344 proto = httpv2protocolhandler(req, ui)
340 345
341 if not wireproto.commands.commandavailable(command, proto):
346 if (not wireproto.commands.commandavailable(command, proto)
347 and command not in extracommands):
342 348 res.status = b'404 Not Found'
343 349 res.headers[b'Content-Type'] = b'text/plain'
344 350 res.setbodybytes(_('invalid wire protocol command: %s') % command)
345 351 return
346 352
347 353 if req.headers.get(b'Accept') != FRAMINGTYPE:
348 354 res.status = b'406 Not Acceptable'
349 355 res.headers[b'Content-Type'] = b'text/plain'
350 356 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
351 357 % FRAMINGTYPE)
352 358 return
353 359
354 360 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
355 361 res.status = b'415 Unsupported Media Type'
356 362 # TODO we should send a response with appropriate media type,
357 363 # since client does Accept it.
358 364 res.headers[b'Content-Type'] = b'text/plain'
359 365 res.setbodybytes(_('client MUST send Content-Type header with '
360 366 'value: %s\n') % FRAMINGTYPE)
361 367 return
362 368
363 369 _processhttpv2request(ui, repo, req, res, permission, command, proto)
364 370
365 371 def _processhttpv2reflectrequest(ui, repo, req, res):
366 372 """Reads unified frame protocol request and dumps out state to client.
367 373
368 374 This special endpoint can be used to help debug the wire protocol.
369 375
370 376 Instead of routing the request through the normal dispatch mechanism,
371 377 we instead read all frames, decode them, and feed them into our state
372 378 tracker. We then dump the log of all that activity back out to the
373 379 client.
374 380 """
375 381 import json
376 382
377 383 # Reflection APIs have a history of being abused, accidentally disclosing
378 384 # sensitive data, etc. So we have a config knob.
379 385 if not ui.configbool('experimental', 'web.api.debugreflect'):
380 386 res.status = b'404 Not Found'
381 387 res.headers[b'Content-Type'] = b'text/plain'
382 388 res.setbodybytes(_('debugreflect service not available'))
383 389 return
384 390
385 391 # We assume we have a unified framing protocol request body.
386 392
387 393 reactor = wireprotoframing.serverreactor()
388 394 states = []
389 395
390 396 while True:
391 397 frame = wireprotoframing.readframe(req.bodyfh)
392 398
393 399 if not frame:
394 400 states.append(b'received: <no frame>')
395 401 break
396 402
397 403 requestid, frametype, frameflags, payload = frame
398 404 states.append(b'received: %d %d %d %s' % (frametype, frameflags,
399 405 requestid, payload))
400 406
401 407 action, meta = reactor.onframerecv(requestid, frametype, frameflags,
402 408 payload)
403 409 states.append(json.dumps((action, meta), sort_keys=True,
404 410 separators=(', ', ': ')))
405 411
406 412 action, meta = reactor.oninputeof()
407 413 meta['action'] = action
408 414 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
409 415
410 416 res.status = b'200 OK'
411 417 res.headers[b'Content-Type'] = b'text/plain'
412 418 res.setbodybytes(b'\n'.join(states))
413 419
414 420 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
415 421 """Post-validation handler for HTTPv2 requests.
416 422
417 423 Called when the HTTP request contains unified frame-based protocol
418 424 frames for evaluation.
419 425 """
420 426 # TODO Some HTTP clients are full duplex and can receive data before
421 427 # the entire request is transmitted. Figure out a way to indicate support
422 428 # for that so we can opt into full duplex mode.
423 429 reactor = wireprotoframing.serverreactor(deferoutput=True)
424 430 seencommand = False
425 431
426 432 while True:
427 433 frame = wireprotoframing.readframe(req.bodyfh)
428 434 if not frame:
429 435 break
430 436
431 437 action, meta = reactor.onframerecv(*frame)
432 438
433 439 if action == 'wantframe':
434 440 # Need more data before we can do anything.
435 441 continue
436 442 elif action == 'runcommand':
437 # We currently only support running a single command per
438 # HTTP request.
439 if seencommand:
440 # TODO define proper error mechanism.
441 res.status = b'200 OK'
442 res.headers[b'Content-Type'] = b'text/plain'
443 res.setbodybytes(_('support for multiple commands per request '
444 'not yet implemented'))
443 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
444 reqcommand, reactor, meta,
445 issubsequent=seencommand)
446
447 if sentoutput:
445 448 return
446 449
447 _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand,
448 reactor, meta)
450 seencommand = True
449 451
450 452 elif action == 'error':
451 453 # TODO define proper error mechanism.
452 454 res.status = b'200 OK'
453 455 res.headers[b'Content-Type'] = b'text/plain'
454 456 res.setbodybytes(meta['message'] + b'\n')
455 457 return
456 458 else:
457 459 raise error.ProgrammingError(
458 460 'unhandled action from frame processor: %s' % action)
459 461
460 462 action, meta = reactor.oninputeof()
461 463 if action == 'sendframes':
462 464 # We assume we haven't started sending the response yet. If we're
463 465 # wrong, the response type will raise an exception.
464 466 res.status = b'200 OK'
465 467 res.headers[b'Content-Type'] = FRAMINGTYPE
466 468 res.setbodygen(meta['framegen'])
467 469 elif action == 'noop':
468 470 pass
469 471 else:
470 472 raise error.ProgrammingError('unhandled action from frame processor: %s'
471 473 % action)
472 474
473 475 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
474 command):
476 command, issubsequent):
475 477 """Dispatch a wire protocol command made from HTTPv2 requests.
476 478
477 479 The authenticated permission (``authedperm``) along with the original
478 480 command from the URL (``reqcommand``) are passed in.
479 481 """
480 482 # We already validated that the session has permissions to perform the
481 483 # actions in ``authedperm``. In the unified frame protocol, the canonical
482 484 # command to run is expressed in a frame. However, the URL also requested
483 485 # to run a specific command. We need to be careful that the command we
484 486 # run doesn't have permissions requirements greater than what was granted
485 487 # by ``authedperm``.
486 488 #
487 # For now, this is no big deal, as we only allow a single command per
488 # request and that command must match the command in the URL. But when
489 # things change, we need to watch out...
490 if reqcommand != command['command']:
491 # TODO define proper error mechanism
492 res.status = b'200 OK'
493 res.headers[b'Content-Type'] = b'text/plain'
494 res.setbodybytes(_('command in frame must match command in URL'))
495 return
496
497 # TODO once we get rid of the command==URL restriction, we'll need to
498 # revalidate command validity and auth here. checkperm,
499 # wireproto.commands.commandavailable(), etc.
489 # Our rule for this is we only allow one command per HTTP request and
490 # that command must match the command in the URL. However, we make
491 # an exception for the ``multirequest`` URL. This URL is allowed to
492 # execute multiple commands. We double check permissions of each command
493 # as it is invoked to ensure there is no privilege escalation.
494 # TODO consider allowing multiple commands to regular command URLs
495 # iff each command is the same.
500 496
501 497 proto = httpv2protocolhandler(req, ui, args=command['args'])
502 assert wireproto.commands.commandavailable(command['command'], proto)
503 wirecommand = wireproto.commands[command['command']]
504 498
505 assert authedperm in (b'ro', b'rw')
506 assert wirecommand.permission in ('push', 'pull')
499 if reqcommand == b'multirequest':
500 if not wireproto.commands.commandavailable(command['command'], proto):
501 # TODO proper error mechanism
502 res.status = b'200 OK'
503 res.headers[b'Content-Type'] = b'text/plain'
504 res.setbodybytes(_('wire protocol command not available: %s') %
505 command['command'])
506 return True
507
508 assert authedperm in (b'ro', b'rw')
509 wirecommand = wireproto.commands[command['command']]
510 assert wirecommand.permission in ('push', 'pull')
507 511
508 # We already checked this as part of the URL==command check, but
509 # permissions are important, so do it again.
510 if authedperm == b'ro':
511 assert wirecommand.permission == 'pull'
512 elif authedperm == b'rw':
513 # We are allowed to access read-only commands under the rw URL.
514 assert wirecommand.permission in ('push', 'pull')
512 if authedperm == b'ro' and wirecommand.permission != 'pull':
513 # TODO proper error mechanism
514 res.status = b'403 Forbidden'
515 res.headers[b'Content-Type'] = b'text/plain'
516 res.setbodybytes(_('insufficient permissions to execute '
517 'command: %s') % command['command'])
518 return True
519
520 # TODO should we also call checkperm() here? Maybe not if we're going
521 # to overhaul that API. The granted scope from the URL check should
522 # be good enough.
523
524 else:
525 # Don't allow multiple commands outside of ``multirequest`` URL.
526 if issubsequent:
527 # TODO proper error mechanism
528 res.status = b'200 OK'
529 res.headers[b'Content-Type'] = b'text/plain'
530 res.setbodybytes(_('multiple commands cannot be issued to this '
531 'URL'))
532 return True
533
534 if reqcommand != command['command']:
535 # TODO define proper error mechanism
536 res.status = b'200 OK'
537 res.headers[b'Content-Type'] = b'text/plain'
538 res.setbodybytes(_('command in frame must match command in URL'))
539 return True
515 540
516 541 rsp = wireproto.dispatch(repo, proto, command['command'])
517 542
518 543 res.status = b'200 OK'
519 544 res.headers[b'Content-Type'] = FRAMINGTYPE
520 545
521 546 if isinstance(rsp, wireprototypes.bytesresponse):
522 547 action, meta = reactor.onbytesresponseready(command['requestid'],
523 548 rsp.data)
524 549 else:
525 550 action, meta = reactor.onapplicationerror(
526 551 _('unhandled response type from wire proto command'))
527 552
528 553 if action == 'sendframes':
529 554 res.setbodygen(meta['framegen'])
555 return True
530 556 elif action == 'noop':
531 557 pass
532 558 else:
533 559 raise error.ProgrammingError('unhandled event from reactor: %s' %
534 560 action)
535 561
536 562 # Maps API name to metadata so custom API can be registered.
537 563 API_HANDLERS = {
538 564 HTTPV2: {
539 565 'config': ('experimental', 'web.api.http-v2'),
540 566 'handler': _handlehttpv2request,
541 567 },
542 568 }
543 569
544 570 class httpv2protocolhandler(wireprototypes.baseprotocolhandler):
545 571 def __init__(self, req, ui, args=None):
546 572 self._req = req
547 573 self._ui = ui
548 574 self._args = args
549 575
550 576 @property
551 577 def name(self):
552 578 return HTTPV2
553 579
554 580 def getargs(self, args):
555 581 data = {}
556 582 for k in args.split():
557 583 if k == '*':
558 584 raise NotImplementedError('do not support * args')
559 585 else:
560 586 data[k] = self._args[k]
561 587
562 588 return [data[k] for k in args.split()]
563 589
564 590 def forwardpayload(self, fp):
565 591 raise NotImplementedError
566 592
567 593 @contextlib.contextmanager
568 594 def mayberedirectstdio(self):
569 595 raise NotImplementedError
570 596
571 597 def client(self):
572 598 raise NotImplementedError
573 599
574 600 def addcapabilities(self, repo, caps):
575 601 return caps
576 602
577 603 def checkperm(self, perm):
578 604 raise NotImplementedError
579 605
580 606 def _httpresponsetype(ui, req, prefer_uncompressed):
581 607 """Determine the appropriate response type and compression settings.
582 608
583 609 Returns a tuple of (mediatype, compengine, engineopts).
584 610 """
585 611 # Determine the response media type and compression engine based
586 612 # on the request parameters.
587 613 protocaps = decodevaluefromheaders(req, 'X-HgProto').split(' ')
588 614
589 615 if '0.2' in protocaps:
590 616 # All clients are expected to support uncompressed data.
591 617 if prefer_uncompressed:
592 618 return HGTYPE2, util._noopengine(), {}
593 619
594 620 # Default as defined by wire protocol spec.
595 621 compformats = ['zlib', 'none']
596 622 for cap in protocaps:
597 623 if cap.startswith('comp='):
598 624 compformats = cap[5:].split(',')
599 625 break
600 626
601 627 # Now find an agreed upon compression format.
602 628 for engine in wireproto.supportedcompengines(ui, util.SERVERROLE):
603 629 if engine.wireprotosupport().name in compformats:
604 630 opts = {}
605 631 level = ui.configint('server', '%slevel' % engine.name())
606 632 if level is not None:
607 633 opts['level'] = level
608 634
609 635 return HGTYPE2, engine, opts
610 636
611 637 # No mutually supported compression format. Fall back to the
612 638 # legacy protocol.
613 639
614 640 # Don't allow untrusted settings because disabling compression or
615 641 # setting a very high compression level could lead to flooding
616 642 # the server's network or CPU.
617 643 opts = {'level': ui.configint('server', 'zliblevel')}
618 644 return HGTYPE, util.compengines['zlib'], opts
619 645
620 646 def _callhttp(repo, req, res, proto, cmd):
621 647 # Avoid cycle involving hg module.
622 648 from .hgweb import common as hgwebcommon
623 649
624 650 def genversion2(gen, engine, engineopts):
625 651 # application/mercurial-0.2 always sends a payload header
626 652 # identifying the compression engine.
627 653 name = engine.wireprotosupport().name
628 654 assert 0 < len(name) < 256
629 655 yield struct.pack('B', len(name))
630 656 yield name
631 657
632 658 for chunk in gen:
633 659 yield chunk
634 660
635 661 def setresponse(code, contenttype, bodybytes=None, bodygen=None):
636 662 if code == HTTP_OK:
637 663 res.status = '200 Script output follows'
638 664 else:
639 665 res.status = hgwebcommon.statusmessage(code)
640 666
641 667 res.headers['Content-Type'] = contenttype
642 668
643 669 if bodybytes is not None:
644 670 res.setbodybytes(bodybytes)
645 671 if bodygen is not None:
646 672 res.setbodygen(bodygen)
647 673
648 674 if not wireproto.commands.commandavailable(cmd, proto):
649 675 setresponse(HTTP_OK, HGERRTYPE,
650 676 _('requested wire protocol command is not available over '
651 677 'HTTP'))
652 678 return
653 679
654 680 proto.checkperm(wireproto.commands[cmd].permission)
655 681
656 682 rsp = wireproto.dispatch(repo, proto, cmd)
657 683
658 684 if isinstance(rsp, bytes):
659 685 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
660 686 elif isinstance(rsp, wireprototypes.bytesresponse):
661 687 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp.data)
662 688 elif isinstance(rsp, wireprototypes.streamreslegacy):
663 689 setresponse(HTTP_OK, HGTYPE, bodygen=rsp.gen)
664 690 elif isinstance(rsp, wireprototypes.streamres):
665 691 gen = rsp.gen
666 692
667 693 # This code for compression should not be streamres specific. It
668 694 # is here because we only compress streamres at the moment.
669 695 mediatype, engine, engineopts = _httpresponsetype(
670 696 repo.ui, req, rsp.prefer_uncompressed)
671 697 gen = engine.compressstream(gen, engineopts)
672 698
673 699 if mediatype == HGTYPE2:
674 700 gen = genversion2(gen, engine, engineopts)
675 701
676 702 setresponse(HTTP_OK, mediatype, bodygen=gen)
677 703 elif isinstance(rsp, wireprototypes.pushres):
678 704 rsp = '%d\n%s' % (rsp.res, rsp.output)
679 705 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
680 706 elif isinstance(rsp, wireprototypes.pusherr):
681 707 rsp = '0\n%s\n' % rsp.res
682 708 res.drain = True
683 709 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
684 710 elif isinstance(rsp, wireprototypes.ooberror):
685 711 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
686 712 else:
687 713 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
688 714
689 715 def _sshv1respondbytes(fout, value):
690 716 """Send a bytes response for protocol version 1."""
691 717 fout.write('%d\n' % len(value))
692 718 fout.write(value)
693 719 fout.flush()
694 720
695 721 def _sshv1respondstream(fout, source):
696 722 write = fout.write
697 723 for chunk in source.gen:
698 724 write(chunk)
699 725 fout.flush()
700 726
701 727 def _sshv1respondooberror(fout, ferr, rsp):
702 728 ferr.write(b'%s\n-\n' % rsp)
703 729 ferr.flush()
704 730 fout.write(b'\n')
705 731 fout.flush()
706 732
707 733 class sshv1protocolhandler(wireprototypes.baseprotocolhandler):
708 734 """Handler for requests services via version 1 of SSH protocol."""
709 735 def __init__(self, ui, fin, fout):
710 736 self._ui = ui
711 737 self._fin = fin
712 738 self._fout = fout
713 739
714 740 @property
715 741 def name(self):
716 742 return wireprototypes.SSHV1
717 743
718 744 def getargs(self, args):
719 745 data = {}
720 746 keys = args.split()
721 747 for n in xrange(len(keys)):
722 748 argline = self._fin.readline()[:-1]
723 749 arg, l = argline.split()
724 750 if arg not in keys:
725 751 raise error.Abort(_("unexpected parameter %r") % arg)
726 752 if arg == '*':
727 753 star = {}
728 754 for k in xrange(int(l)):
729 755 argline = self._fin.readline()[:-1]
730 756 arg, l = argline.split()
731 757 val = self._fin.read(int(l))
732 758 star[arg] = val
733 759 data['*'] = star
734 760 else:
735 761 val = self._fin.read(int(l))
736 762 data[arg] = val
737 763 return [data[k] for k in keys]
738 764
739 765 def forwardpayload(self, fpout):
740 766 # We initially send an empty response. This tells the client it is
741 767 # OK to start sending data. If a client sees any other response, it
742 768 # interprets it as an error.
743 769 _sshv1respondbytes(self._fout, b'')
744 770
745 771 # The file is in the form:
746 772 #
747 773 # <chunk size>\n<chunk>
748 774 # ...
749 775 # 0\n
750 776 count = int(self._fin.readline())
751 777 while count:
752 778 fpout.write(self._fin.read(count))
753 779 count = int(self._fin.readline())
754 780
755 781 @contextlib.contextmanager
756 782 def mayberedirectstdio(self):
757 783 yield None
758 784
759 785 def client(self):
760 786 client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
761 787 return 'remote:ssh:' + client
762 788
763 789 def addcapabilities(self, repo, caps):
764 790 caps.append(b'batch')
765 791 return caps
766 792
767 793 def checkperm(self, perm):
768 794 pass
769 795
770 796 class sshv2protocolhandler(sshv1protocolhandler):
771 797 """Protocol handler for version 2 of the SSH protocol."""
772 798
773 799 @property
774 800 def name(self):
775 801 return wireprototypes.SSHV2
776 802
777 803 def _runsshserver(ui, repo, fin, fout, ev):
778 804 # This function operates like a state machine of sorts. The following
779 805 # states are defined:
780 806 #
781 807 # protov1-serving
782 808 # Server is in protocol version 1 serving mode. Commands arrive on
783 809 # new lines. These commands are processed in this state, one command
784 810 # after the other.
785 811 #
786 812 # protov2-serving
787 813 # Server is in protocol version 2 serving mode.
788 814 #
789 815 # upgrade-initial
790 816 # The server is going to process an upgrade request.
791 817 #
792 818 # upgrade-v2-filter-legacy-handshake
793 819 # The protocol is being upgraded to version 2. The server is expecting
794 820 # the legacy handshake from version 1.
795 821 #
796 822 # upgrade-v2-finish
797 823 # The upgrade to version 2 of the protocol is imminent.
798 824 #
799 825 # shutdown
800 826 # The server is shutting down, possibly in reaction to a client event.
801 827 #
802 828 # And here are their transitions:
803 829 #
804 830 # protov1-serving -> shutdown
805 831 # When server receives an empty request or encounters another
806 832 # error.
807 833 #
808 834 # protov1-serving -> upgrade-initial
809 835 # An upgrade request line was seen.
810 836 #
811 837 # upgrade-initial -> upgrade-v2-filter-legacy-handshake
812 838 # Upgrade to version 2 in progress. Server is expecting to
813 839 # process a legacy handshake.
814 840 #
815 841 # upgrade-v2-filter-legacy-handshake -> shutdown
816 842 # Client did not fulfill upgrade handshake requirements.
817 843 #
818 844 # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
819 845 # Client fulfilled version 2 upgrade requirements. Finishing that
820 846 # upgrade.
821 847 #
822 848 # upgrade-v2-finish -> protov2-serving
823 849 # Protocol upgrade to version 2 complete. Server can now speak protocol
824 850 # version 2.
825 851 #
826 852 # protov2-serving -> protov1-serving
827 853 # Ths happens by default since protocol version 2 is the same as
828 854 # version 1 except for the handshake.
829 855
830 856 state = 'protov1-serving'
831 857 proto = sshv1protocolhandler(ui, fin, fout)
832 858 protoswitched = False
833 859
834 860 while not ev.is_set():
835 861 if state == 'protov1-serving':
836 862 # Commands are issued on new lines.
837 863 request = fin.readline()[:-1]
838 864
839 865 # Empty lines signal to terminate the connection.
840 866 if not request:
841 867 state = 'shutdown'
842 868 continue
843 869
844 870 # It looks like a protocol upgrade request. Transition state to
845 871 # handle it.
846 872 if request.startswith(b'upgrade '):
847 873 if protoswitched:
848 874 _sshv1respondooberror(fout, ui.ferr,
849 875 b'cannot upgrade protocols multiple '
850 876 b'times')
851 877 state = 'shutdown'
852 878 continue
853 879
854 880 state = 'upgrade-initial'
855 881 continue
856 882
857 883 available = wireproto.commands.commandavailable(request, proto)
858 884
859 885 # This command isn't available. Send an empty response and go
860 886 # back to waiting for a new command.
861 887 if not available:
862 888 _sshv1respondbytes(fout, b'')
863 889 continue
864 890
865 891 rsp = wireproto.dispatch(repo, proto, request)
866 892
867 893 if isinstance(rsp, bytes):
868 894 _sshv1respondbytes(fout, rsp)
869 895 elif isinstance(rsp, wireprototypes.bytesresponse):
870 896 _sshv1respondbytes(fout, rsp.data)
871 897 elif isinstance(rsp, wireprototypes.streamres):
872 898 _sshv1respondstream(fout, rsp)
873 899 elif isinstance(rsp, wireprototypes.streamreslegacy):
874 900 _sshv1respondstream(fout, rsp)
875 901 elif isinstance(rsp, wireprototypes.pushres):
876 902 _sshv1respondbytes(fout, b'')
877 903 _sshv1respondbytes(fout, b'%d' % rsp.res)
878 904 elif isinstance(rsp, wireprototypes.pusherr):
879 905 _sshv1respondbytes(fout, rsp.res)
880 906 elif isinstance(rsp, wireprototypes.ooberror):
881 907 _sshv1respondooberror(fout, ui.ferr, rsp.message)
882 908 else:
883 909 raise error.ProgrammingError('unhandled response type from '
884 910 'wire protocol command: %s' % rsp)
885 911
886 912 # For now, protocol version 2 serving just goes back to version 1.
887 913 elif state == 'protov2-serving':
888 914 state = 'protov1-serving'
889 915 continue
890 916
891 917 elif state == 'upgrade-initial':
892 918 # We should never transition into this state if we've switched
893 919 # protocols.
894 920 assert not protoswitched
895 921 assert proto.name == wireprototypes.SSHV1
896 922
897 923 # Expected: upgrade <token> <capabilities>
898 924 # If we get something else, the request is malformed. It could be
899 925 # from a future client that has altered the upgrade line content.
900 926 # We treat this as an unknown command.
901 927 try:
902 928 token, caps = request.split(b' ')[1:]
903 929 except ValueError:
904 930 _sshv1respondbytes(fout, b'')
905 931 state = 'protov1-serving'
906 932 continue
907 933
908 934 # Send empty response if we don't support upgrading protocols.
909 935 if not ui.configbool('experimental', 'sshserver.support-v2'):
910 936 _sshv1respondbytes(fout, b'')
911 937 state = 'protov1-serving'
912 938 continue
913 939
914 940 try:
915 941 caps = urlreq.parseqs(caps)
916 942 except ValueError:
917 943 _sshv1respondbytes(fout, b'')
918 944 state = 'protov1-serving'
919 945 continue
920 946
921 947 # We don't see an upgrade request to protocol version 2. Ignore
922 948 # the upgrade request.
923 949 wantedprotos = caps.get(b'proto', [b''])[0]
924 950 if SSHV2 not in wantedprotos:
925 951 _sshv1respondbytes(fout, b'')
926 952 state = 'protov1-serving'
927 953 continue
928 954
929 955 # It looks like we can honor this upgrade request to protocol 2.
930 956 # Filter the rest of the handshake protocol request lines.
931 957 state = 'upgrade-v2-filter-legacy-handshake'
932 958 continue
933 959
934 960 elif state == 'upgrade-v2-filter-legacy-handshake':
935 961 # Client should have sent legacy handshake after an ``upgrade``
936 962 # request. Expected lines:
937 963 #
938 964 # hello
939 965 # between
940 966 # pairs 81
941 967 # 0000...-0000...
942 968
943 969 ok = True
944 970 for line in (b'hello', b'between', b'pairs 81'):
945 971 request = fin.readline()[:-1]
946 972
947 973 if request != line:
948 974 _sshv1respondooberror(fout, ui.ferr,
949 975 b'malformed handshake protocol: '
950 976 b'missing %s' % line)
951 977 ok = False
952 978 state = 'shutdown'
953 979 break
954 980
955 981 if not ok:
956 982 continue
957 983
958 984 request = fin.read(81)
959 985 if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
960 986 _sshv1respondooberror(fout, ui.ferr,
961 987 b'malformed handshake protocol: '
962 988 b'missing between argument value')
963 989 state = 'shutdown'
964 990 continue
965 991
966 992 state = 'upgrade-v2-finish'
967 993 continue
968 994
969 995 elif state == 'upgrade-v2-finish':
970 996 # Send the upgrade response.
971 997 fout.write(b'upgraded %s %s\n' % (token, SSHV2))
972 998 servercaps = wireproto.capabilities(repo, proto)
973 999 rsp = b'capabilities: %s' % servercaps.data
974 1000 fout.write(b'%d\n%s\n' % (len(rsp), rsp))
975 1001 fout.flush()
976 1002
977 1003 proto = sshv2protocolhandler(ui, fin, fout)
978 1004 protoswitched = True
979 1005
980 1006 state = 'protov2-serving'
981 1007 continue
982 1008
983 1009 elif state == 'shutdown':
984 1010 break
985 1011
986 1012 else:
987 1013 raise error.ProgrammingError('unhandled ssh server state: %s' %
988 1014 state)
989 1015
990 1016 class sshserver(object):
991 1017 def __init__(self, ui, repo, logfh=None):
992 1018 self._ui = ui
993 1019 self._repo = repo
994 1020 self._fin = ui.fin
995 1021 self._fout = ui.fout
996 1022
997 1023 # Log write I/O to stdout and stderr if configured.
998 1024 if logfh:
999 1025 self._fout = util.makeloggingfileobject(
1000 1026 logfh, self._fout, 'o', logdata=True)
1001 1027 ui.ferr = util.makeloggingfileobject(
1002 1028 logfh, ui.ferr, 'e', logdata=True)
1003 1029
1004 1030 hook.redirect(True)
1005 1031 ui.fout = repo.ui.fout = ui.ferr
1006 1032
1007 1033 # Prevent insertion/deletion of CRs
1008 1034 util.setbinary(self._fin)
1009 1035 util.setbinary(self._fout)
1010 1036
1011 1037 def serve_forever(self):
1012 1038 self.serveuntil(threading.Event())
1013 1039 sys.exit(0)
1014 1040
1015 1041 def serveuntil(self, ev):
1016 1042 """Serve until a threading.Event is set."""
1017 1043 _runsshserver(self._ui, self._repo, self._fin, self._fout, ev)
@@ -1,415 +1,564 b''
1 1 $ HTTPV2=exp-http-v2-0001
2 2 $ MEDIATYPE=application/mercurial-exp-framing-0002
3 3
4 4 $ send() {
5 5 > hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/
6 6 > }
7 7
8 8 $ cat > dummycommands.py << EOF
9 9 > from mercurial import wireprototypes, wireproto
10 10 > @wireproto.wireprotocommand('customreadonly', permission='pull')
11 11 > def customreadonly(repo, proto):
12 12 > return wireprototypes.bytesresponse(b'customreadonly bytes response')
13 13 > @wireproto.wireprotocommand('customreadwrite', permission='push')
14 14 > def customreadwrite(repo, proto):
15 15 > return wireprototypes.bytesresponse(b'customreadwrite bytes response')
16 16 > EOF
17 17
18 18 $ cat >> $HGRCPATH << EOF
19 19 > [extensions]
20 20 > dummycommands = $TESTTMP/dummycommands.py
21 21 > EOF
22 22
23 23 $ hg init server
24 24 $ cat > server/.hg/hgrc << EOF
25 25 > [experimental]
26 26 > web.apiserver = true
27 27 > EOF
28 28 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
29 29 $ cat hg.pid > $DAEMON_PIDS
30 30
31 31 HTTP v2 protocol not enabled by default
32 32
33 33 $ send << EOF
34 34 > httprequest GET api/$HTTPV2
35 35 > user-agent: test
36 36 > EOF
37 37 using raw connection to peer
38 38 s> GET /api/exp-http-v2-0001 HTTP/1.1\r\n
39 39 s> Accept-Encoding: identity\r\n
40 40 s> user-agent: test\r\n
41 41 s> host: $LOCALIP:$HGPORT\r\n (glob)
42 42 s> \r\n
43 43 s> makefile('rb', None)
44 44 s> HTTP/1.1 404 Not Found\r\n
45 45 s> Server: testing stub value\r\n
46 46 s> Date: $HTTP_DATE$\r\n
47 47 s> Content-Type: text/plain\r\n
48 48 s> Content-Length: 33\r\n
49 49 s> \r\n
50 50 s> API exp-http-v2-0001 not enabled\n
51 51
52 52 Restart server with support for HTTP v2 API
53 53
54 54 $ killdaemons.py
55 55 $ cat > server/.hg/hgrc << EOF
56 56 > [experimental]
57 57 > web.apiserver = true
58 58 > web.api.http-v2 = true
59 59 > EOF
60 60
61 61 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
62 62 $ cat hg.pid > $DAEMON_PIDS
63 63
64 64 Request to unknown command yields 404
65 65
66 66 $ send << EOF
67 67 > httprequest POST api/$HTTPV2/ro/badcommand
68 68 > user-agent: test
69 69 > EOF
70 70 using raw connection to peer
71 71 s> POST /api/exp-http-v2-0001/ro/badcommand HTTP/1.1\r\n
72 72 s> Accept-Encoding: identity\r\n
73 73 s> user-agent: test\r\n
74 74 s> host: $LOCALIP:$HGPORT\r\n (glob)
75 75 s> \r\n
76 76 s> makefile('rb', None)
77 77 s> HTTP/1.1 404 Not Found\r\n
78 78 s> Server: testing stub value\r\n
79 79 s> Date: $HTTP_DATE$\r\n
80 80 s> Content-Type: text/plain\r\n
81 81 s> Content-Length: 42\r\n
82 82 s> \r\n
83 83 s> unknown wire protocol command: badcommand\n
84 84
85 85 GET to read-only command yields a 405
86 86
87 87 $ send << EOF
88 88 > httprequest GET api/$HTTPV2/ro/customreadonly
89 89 > user-agent: test
90 90 > EOF
91 91 using raw connection to peer
92 92 s> GET /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
93 93 s> Accept-Encoding: identity\r\n
94 94 s> user-agent: test\r\n
95 95 s> host: $LOCALIP:$HGPORT\r\n (glob)
96 96 s> \r\n
97 97 s> makefile('rb', None)
98 98 s> HTTP/1.1 405 Method Not Allowed\r\n
99 99 s> Server: testing stub value\r\n
100 100 s> Date: $HTTP_DATE$\r\n
101 101 s> Allow: POST\r\n
102 102 s> Content-Length: 30\r\n
103 103 s> \r\n
104 104 s> commands require POST requests
105 105
106 106 Missing Accept header results in 406
107 107
108 108 $ send << EOF
109 109 > httprequest POST api/$HTTPV2/ro/customreadonly
110 110 > user-agent: test
111 111 > EOF
112 112 using raw connection to peer
113 113 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
114 114 s> Accept-Encoding: identity\r\n
115 115 s> user-agent: test\r\n
116 116 s> host: $LOCALIP:$HGPORT\r\n (glob)
117 117 s> \r\n
118 118 s> makefile('rb', None)
119 119 s> HTTP/1.1 406 Not Acceptable\r\n
120 120 s> Server: testing stub value\r\n
121 121 s> Date: $HTTP_DATE$\r\n
122 122 s> Content-Type: text/plain\r\n
123 123 s> Content-Length: 85\r\n
124 124 s> \r\n
125 125 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0002\n
126 126
127 127 Bad Accept header results in 406
128 128
129 129 $ send << EOF
130 130 > httprequest POST api/$HTTPV2/ro/customreadonly
131 131 > accept: invalid
132 132 > user-agent: test
133 133 > EOF
134 134 using raw connection to peer
135 135 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
136 136 s> Accept-Encoding: identity\r\n
137 137 s> accept: invalid\r\n
138 138 s> user-agent: test\r\n
139 139 s> host: $LOCALIP:$HGPORT\r\n (glob)
140 140 s> \r\n
141 141 s> makefile('rb', None)
142 142 s> HTTP/1.1 406 Not Acceptable\r\n
143 143 s> Server: testing stub value\r\n
144 144 s> Date: $HTTP_DATE$\r\n
145 145 s> Content-Type: text/plain\r\n
146 146 s> Content-Length: 85\r\n
147 147 s> \r\n
148 148 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0002\n
149 149
150 150 Bad Content-Type header results in 415
151 151
152 152 $ send << EOF
153 153 > httprequest POST api/$HTTPV2/ro/customreadonly
154 154 > accept: $MEDIATYPE
155 155 > user-agent: test
156 156 > content-type: badmedia
157 157 > EOF
158 158 using raw connection to peer
159 159 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
160 160 s> Accept-Encoding: identity\r\n
161 161 s> accept: application/mercurial-exp-framing-0002\r\n
162 162 s> content-type: badmedia\r\n
163 163 s> user-agent: test\r\n
164 164 s> host: $LOCALIP:$HGPORT\r\n (glob)
165 165 s> \r\n
166 166 s> makefile('rb', None)
167 167 s> HTTP/1.1 415 Unsupported Media Type\r\n
168 168 s> Server: testing stub value\r\n
169 169 s> Date: $HTTP_DATE$\r\n
170 170 s> Content-Type: text/plain\r\n
171 171 s> Content-Length: 88\r\n
172 172 s> \r\n
173 173 s> client MUST send Content-Type header with value: application/mercurial-exp-framing-0002\n
174 174
175 175 Request to read-only command works out of the box
176 176
177 177 $ send << EOF
178 178 > httprequest POST api/$HTTPV2/ro/customreadonly
179 179 > accept: $MEDIATYPE
180 180 > content-type: $MEDIATYPE
181 181 > user-agent: test
182 182 > frame 1 command-name eos customreadonly
183 183 > EOF
184 184 using raw connection to peer
185 185 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
186 186 s> Accept-Encoding: identity\r\n
187 187 s> accept: application/mercurial-exp-framing-0002\r\n
188 188 s> content-type: application/mercurial-exp-framing-0002\r\n
189 189 s> user-agent: test\r\n
190 190 s> *\r\n (glob)
191 191 s> host: $LOCALIP:$HGPORT\r\n (glob)
192 192 s> \r\n
193 193 s> \x0e\x00\x00\x01\x00\x11customreadonly
194 194 s> makefile('rb', None)
195 195 s> HTTP/1.1 200 OK\r\n
196 196 s> Server: testing stub value\r\n
197 197 s> Date: $HTTP_DATE$\r\n
198 198 s> Content-Type: application/mercurial-exp-framing-0002\r\n
199 199 s> Transfer-Encoding: chunked\r\n
200 200 s> \r\n
201 201 s> 23\r\n
202 202 s> \x1d\x00\x00\x01\x00Bcustomreadonly bytes response
203 203 s> \r\n
204 204 s> 0\r\n
205 205 s> \r\n
206 206
207 207 Request to read-write command fails because server is read-only by default
208 208
209 209 GET to read-write request yields 405
210 210
211 211 $ send << EOF
212 212 > httprequest GET api/$HTTPV2/rw/customreadonly
213 213 > user-agent: test
214 214 > EOF
215 215 using raw connection to peer
216 216 s> GET /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
217 217 s> Accept-Encoding: identity\r\n
218 218 s> user-agent: test\r\n
219 219 s> host: $LOCALIP:$HGPORT\r\n (glob)
220 220 s> \r\n
221 221 s> makefile('rb', None)
222 222 s> HTTP/1.1 405 Method Not Allowed\r\n
223 223 s> Server: testing stub value\r\n
224 224 s> Date: $HTTP_DATE$\r\n
225 225 s> Allow: POST\r\n
226 226 s> Content-Length: 30\r\n
227 227 s> \r\n
228 228 s> commands require POST requests
229 229
230 230 Even for unknown commands
231 231
232 232 $ send << EOF
233 233 > httprequest GET api/$HTTPV2/rw/badcommand
234 234 > user-agent: test
235 235 > EOF
236 236 using raw connection to peer
237 237 s> GET /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
238 238 s> Accept-Encoding: identity\r\n
239 239 s> user-agent: test\r\n
240 240 s> host: $LOCALIP:$HGPORT\r\n (glob)
241 241 s> \r\n
242 242 s> makefile('rb', None)
243 243 s> HTTP/1.1 405 Method Not Allowed\r\n
244 244 s> Server: testing stub value\r\n
245 245 s> Date: $HTTP_DATE$\r\n
246 246 s> Allow: POST\r\n
247 247 s> Content-Length: 30\r\n
248 248 s> \r\n
249 249 s> commands require POST requests
250 250
251 251 SSL required by default
252 252
253 253 $ send << EOF
254 254 > httprequest POST api/$HTTPV2/rw/customreadonly
255 255 > user-agent: test
256 256 > EOF
257 257 using raw connection to peer
258 258 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
259 259 s> Accept-Encoding: identity\r\n
260 260 s> user-agent: test\r\n
261 261 s> host: $LOCALIP:$HGPORT\r\n (glob)
262 262 s> \r\n
263 263 s> makefile('rb', None)
264 264 s> HTTP/1.1 403 ssl required\r\n
265 265 s> Server: testing stub value\r\n
266 266 s> Date: $HTTP_DATE$\r\n
267 267 s> Content-Length: 17\r\n
268 268 s> \r\n
269 269 s> permission denied
270 270
271 271 Restart server to allow non-ssl read-write operations
272 272
273 273 $ killdaemons.py
274 274 $ cat > server/.hg/hgrc << EOF
275 275 > [experimental]
276 276 > web.apiserver = true
277 277 > web.api.http-v2 = true
278 278 > [web]
279 279 > push_ssl = false
280 280 > allow-push = *
281 281 > EOF
282 282
283 283 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
284 284 $ cat hg.pid > $DAEMON_PIDS
285 285
286 286 Authorized request for valid read-write command works
287 287
288 288 $ send << EOF
289 289 > httprequest POST api/$HTTPV2/rw/customreadonly
290 290 > user-agent: test
291 291 > accept: $MEDIATYPE
292 292 > content-type: $MEDIATYPE
293 293 > frame 1 command-name eos customreadonly
294 294 > EOF
295 295 using raw connection to peer
296 296 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
297 297 s> Accept-Encoding: identity\r\n
298 298 s> accept: application/mercurial-exp-framing-0002\r\n
299 299 s> content-type: application/mercurial-exp-framing-0002\r\n
300 300 s> user-agent: test\r\n
301 301 s> content-length: 20\r\n
302 302 s> host: $LOCALIP:$HGPORT\r\n (glob)
303 303 s> \r\n
304 304 s> \x0e\x00\x00\x01\x00\x11customreadonly
305 305 s> makefile('rb', None)
306 306 s> HTTP/1.1 200 OK\r\n
307 307 s> Server: testing stub value\r\n
308 308 s> Date: $HTTP_DATE$\r\n
309 309 s> Content-Type: application/mercurial-exp-framing-0002\r\n
310 310 s> Transfer-Encoding: chunked\r\n
311 311 s> \r\n
312 312 s> 23\r\n
313 313 s> \x1d\x00\x00\x01\x00Bcustomreadonly bytes response
314 314 s> \r\n
315 315 s> 0\r\n
316 316 s> \r\n
317 317
318 318 Authorized request for unknown command is rejected
319 319
320 320 $ send << EOF
321 321 > httprequest POST api/$HTTPV2/rw/badcommand
322 322 > user-agent: test
323 323 > accept: $MEDIATYPE
324 324 > EOF
325 325 using raw connection to peer
326 326 s> POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
327 327 s> Accept-Encoding: identity\r\n
328 328 s> accept: application/mercurial-exp-framing-0002\r\n
329 329 s> user-agent: test\r\n
330 330 s> host: $LOCALIP:$HGPORT\r\n (glob)
331 331 s> \r\n
332 332 s> makefile('rb', None)
333 333 s> HTTP/1.1 404 Not Found\r\n
334 334 s> Server: testing stub value\r\n
335 335 s> Date: $HTTP_DATE$\r\n
336 336 s> Content-Type: text/plain\r\n
337 337 s> Content-Length: 42\r\n
338 338 s> \r\n
339 339 s> unknown wire protocol command: badcommand\n
340 340
341 341 debugreflect isn't enabled by default
342 342
343 343 $ send << EOF
344 344 > httprequest POST api/$HTTPV2/ro/debugreflect
345 345 > user-agent: test
346 346 > EOF
347 347 using raw connection to peer
348 348 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
349 349 s> Accept-Encoding: identity\r\n
350 350 s> user-agent: test\r\n
351 351 s> host: $LOCALIP:$HGPORT\r\n (glob)
352 352 s> \r\n
353 353 s> makefile('rb', None)
354 354 s> HTTP/1.1 404 Not Found\r\n
355 355 s> Server: testing stub value\r\n
356 356 s> Date: $HTTP_DATE$\r\n
357 357 s> Content-Type: text/plain\r\n
358 358 s> Content-Length: 34\r\n
359 359 s> \r\n
360 360 s> debugreflect service not available
361 361
362 362 Restart server to get debugreflect endpoint
363 363
364 364 $ killdaemons.py
365 365 $ cat > server/.hg/hgrc << EOF
366 366 > [experimental]
367 367 > web.apiserver = true
368 368 > web.api.debugreflect = true
369 369 > web.api.http-v2 = true
370 370 > [web]
371 371 > push_ssl = false
372 372 > allow-push = *
373 373 > EOF
374 374
375 375 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
376 376 $ cat hg.pid > $DAEMON_PIDS
377 377
378 378 Command frames can be reflected via debugreflect
379 379
380 380 $ send << EOF
381 381 > httprequest POST api/$HTTPV2/ro/debugreflect
382 382 > accept: $MEDIATYPE
383 383 > content-type: $MEDIATYPE
384 384 > user-agent: test
385 385 > frame 1 command-name have-args command1
386 386 > frame 1 command-argument 0 \x03\x00\x04\x00fooval1
387 387 > frame 1 command-argument eoa \x04\x00\x03\x00bar1val
388 388 > EOF
389 389 using raw connection to peer
390 390 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
391 391 s> Accept-Encoding: identity\r\n
392 392 s> accept: application/mercurial-exp-framing-0002\r\n
393 393 s> content-type: application/mercurial-exp-framing-0002\r\n
394 394 s> user-agent: test\r\n
395 395 s> content-length: 48\r\n
396 396 s> host: $LOCALIP:$HGPORT\r\n (glob)
397 397 s> \r\n
398 398 s> \x08\x00\x00\x01\x00\x12command1\x0b\x00\x00\x01\x00 \x03\x00\x04\x00fooval1\x0b\x00\x00\x01\x00"\x04\x00\x03\x00bar1val
399 399 s> makefile('rb', None)
400 400 s> HTTP/1.1 200 OK\r\n
401 401 s> Server: testing stub value\r\n
402 402 s> Date: $HTTP_DATE$\r\n
403 403 s> Content-Type: text/plain\r\n
404 404 s> Content-Length: 322\r\n
405 405 s> \r\n
406 406 s> received: 1 2 1 command1\n
407 407 s> ["wantframe", {"state": "command-receiving"}]\n
408 408 s> received: 2 0 1 \x03\x00\x04\x00fooval1\n
409 409 s> ["wantframe", {"state": "command-receiving"}]\n
410 410 s> received: 2 2 1 \x04\x00\x03\x00bar1val\n
411 411 s> ["runcommand", {"args": {"bar1": "val", "foo": "val1"}, "command": "command1", "data": null, "requestid": 1}]\n
412 412 s> received: <no frame>\n
413 413 s> {"action": "noop"}
414 414
415 Multiple requests to regular command URL are not allowed
416
417 $ send << EOF
418 > httprequest POST api/$HTTPV2/ro/customreadonly
419 > accept: $MEDIATYPE
420 > content-type: $MEDIATYPE
421 > user-agent: test
422 > frame 1 command-name eos customreadonly
423 > frame 3 command-name eos customreadonly
424 > EOF
425 using raw connection to peer
426 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
427 s> Accept-Encoding: identity\r\n
428 s> accept: application/mercurial-exp-framing-0002\r\n
429 s> content-type: application/mercurial-exp-framing-0002\r\n
430 s> user-agent: test\r\n
431 s> content-length: 40\r\n
432 s> host: $LOCALIP:$HGPORT\r\n (glob)
433 s> \r\n
434 s> \x0e\x00\x00\x01\x00\x11customreadonly\x0e\x00\x00\x03\x00\x11customreadonly
435 s> makefile('rb', None)
436 s> HTTP/1.1 200 OK\r\n
437 s> Server: testing stub value\r\n
438 s> Date: $HTTP_DATE$\r\n
439 s> Content-Type: text/plain\r\n
440 s> Content-Length: 46\r\n
441 s> \r\n
442 s> multiple commands cannot be issued to this URL
443
444 Multiple requests to "multirequest" URL are allowed
445
446 $ send << EOF
447 > httprequest POST api/$HTTPV2/ro/multirequest
448 > accept: $MEDIATYPE
449 > content-type: $MEDIATYPE
450 > user-agent: test
451 > frame 1 command-name eos customreadonly
452 > frame 3 command-name eos customreadonly
453 > EOF
454 using raw connection to peer
455 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
456 s> Accept-Encoding: identity\r\n
457 s> accept: application/mercurial-exp-framing-0002\r\n
458 s> content-type: application/mercurial-exp-framing-0002\r\n
459 s> user-agent: test\r\n
460 s> *\r\n (glob)
461 s> host: $LOCALIP:$HGPORT\r\n (glob)
462 s> \r\n
463 s> \x0e\x00\x00\x01\x00\x11customreadonly\x0e\x00\x00\x03\x00\x11customreadonly
464 s> makefile('rb', None)
465 s> HTTP/1.1 200 OK\r\n
466 s> Server: testing stub value\r\n
467 s> Date: $HTTP_DATE$\r\n
468 s> Content-Type: application/mercurial-exp-framing-0002\r\n
469 s> Transfer-Encoding: chunked\r\n
470 s> \r\n
471 s> *\r\n (glob)
472 s> \x1d\x00\x00\x01\x00Bcustomreadonly bytes response
473 s> \r\n
474 s> 23\r\n
475 s> \x1d\x00\x00\x03\x00Bcustomreadonly bytes response
476 s> \r\n
477 s> 0\r\n
478 s> \r\n
479
480 Interleaved requests to "multirequest" are processed
481
482 $ send << EOF
483 > httprequest POST api/$HTTPV2/ro/multirequest
484 > accept: $MEDIATYPE
485 > content-type: $MEDIATYPE
486 > user-agent: test
487 > frame 1 command-name have-args listkeys
488 > frame 3 command-name have-args listkeys
489 > frame 3 command-argument eoa \x09\x00\x09\x00namespacebookmarks
490 > frame 1 command-argument eoa \x09\x00\x0a\x00namespacenamespaces
491 > EOF
492 using raw connection to peer
493 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
494 s> Accept-Encoding: identity\r\n
495 s> accept: application/mercurial-exp-framing-0002\r\n
496 s> content-type: application/mercurial-exp-framing-0002\r\n
497 s> user-agent: test\r\n
498 s> content-length: 85\r\n
499 s> host: $LOCALIP:$HGPORT\r\n (glob)
500 s> \r\n
501 s> \x08\x00\x00\x01\x00\x12listkeys\x08\x00\x00\x03\x00\x12listkeys\x16\x00\x00\x03\x00" \x00 \x00namespacebookmarks\x17\x00\x00\x01\x00" \x00\n
502 s> \x00namespacenamespaces
503 s> makefile('rb', None)
504 s> HTTP/1.1 200 OK\r\n
505 s> Server: testing stub value\r\n
506 s> Date: $HTTP_DATE$\r\n
507 s> Content-Type: application/mercurial-exp-framing-0002\r\n
508 s> Transfer-Encoding: chunked\r\n
509 s> \r\n
510 s> 6\r\n
511 s> \x00\x00\x00\x03\x00B
512 s> \r\n
513 s> 24\r\n
514 s> \x1e\x00\x00\x01\x00Bbookmarks \n
515 s> namespaces \n
516 s> phases
517 s> \r\n
518 s> 0\r\n
519 s> \r\n
520
521 Restart server to disable read-write access
522
523 $ killdaemons.py
524 $ cat > server/.hg/hgrc << EOF
525 > [experimental]
526 > web.apiserver = true
527 > web.api.debugreflect = true
528 > web.api.http-v2 = true
529 > [web]
530 > push_ssl = false
531 > EOF
532
533 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
534 $ cat hg.pid > $DAEMON_PIDS
535
536 Attempting to run a read-write command via multirequest on read-only URL is not allowed
537
538 $ send << EOF
539 > httprequest POST api/$HTTPV2/ro/multirequest
540 > accept: $MEDIATYPE
541 > content-type: $MEDIATYPE
542 > user-agent: test
543 > frame 1 command-name eos unbundle
544 > EOF
545 using raw connection to peer
546 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
547 s> Accept-Encoding: identity\r\n
548 s> accept: application/mercurial-exp-framing-0002\r\n
549 s> content-type: application/mercurial-exp-framing-0002\r\n
550 s> user-agent: test\r\n
551 s> content-length: 14\r\n
552 s> host: $LOCALIP:$HGPORT\r\n (glob)
553 s> \r\n
554 s> \x08\x00\x00\x01\x00\x11unbundle
555 s> makefile('rb', None)
556 s> HTTP/1.1 403 Forbidden\r\n
557 s> Server: testing stub value\r\n
558 s> Date: $HTTP_DATE$\r\n
559 s> Content-Type: text/plain\r\n
560 s> Content-Length: 53\r\n
561 s> \r\n
562 s> insufficient permissions to execute command: unbundle
563
415 564 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now