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