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