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