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