##// END OF EJS Templates
wireprotov2server: use our CBOR encoder...
Gregory Szorc -
r39479:660879e4 default
parent child Browse files
Show More
@@ -1,534 +1,535 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
10
11 from .i18n import _
11 from .i18n import _
12 from .thirdparty import (
13 cbor,
14 )
15 from . import (
12 from . import (
16 encoding,
13 encoding,
17 error,
14 error,
18 pycompat,
15 pycompat,
19 streamclone,
16 streamclone,
20 util,
17 util,
21 wireprotoframing,
18 wireprotoframing,
22 wireprototypes,
19 wireprototypes,
23 )
20 )
24 from .utils import (
21 from .utils import (
22 cborutil,
25 interfaceutil,
23 interfaceutil,
26 )
24 )
27
25
28 FRAMINGTYPE = b'application/mercurial-exp-framing-0005'
26 FRAMINGTYPE = b'application/mercurial-exp-framing-0005'
29
27
30 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
28 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
31
29
32 COMMANDS = wireprototypes.commanddict()
30 COMMANDS = wireprototypes.commanddict()
33
31
34 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
32 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
35 from .hgweb import common as hgwebcommon
33 from .hgweb import common as hgwebcommon
36
34
37 # URL space looks like: <permissions>/<command>, where <permission> can
35 # URL space looks like: <permissions>/<command>, where <permission> can
38 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
36 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
39
37
40 # Root URL does nothing meaningful... yet.
38 # Root URL does nothing meaningful... yet.
41 if not urlparts:
39 if not urlparts:
42 res.status = b'200 OK'
40 res.status = b'200 OK'
43 res.headers[b'Content-Type'] = b'text/plain'
41 res.headers[b'Content-Type'] = b'text/plain'
44 res.setbodybytes(_('HTTP version 2 API handler'))
42 res.setbodybytes(_('HTTP version 2 API handler'))
45 return
43 return
46
44
47 if len(urlparts) == 1:
45 if len(urlparts) == 1:
48 res.status = b'404 Not Found'
46 res.status = b'404 Not Found'
49 res.headers[b'Content-Type'] = b'text/plain'
47 res.headers[b'Content-Type'] = b'text/plain'
50 res.setbodybytes(_('do not know how to process %s\n') %
48 res.setbodybytes(_('do not know how to process %s\n') %
51 req.dispatchpath)
49 req.dispatchpath)
52 return
50 return
53
51
54 permission, command = urlparts[0:2]
52 permission, command = urlparts[0:2]
55
53
56 if permission not in (b'ro', b'rw'):
54 if permission not in (b'ro', b'rw'):
57 res.status = b'404 Not Found'
55 res.status = b'404 Not Found'
58 res.headers[b'Content-Type'] = b'text/plain'
56 res.headers[b'Content-Type'] = b'text/plain'
59 res.setbodybytes(_('unknown permission: %s') % permission)
57 res.setbodybytes(_('unknown permission: %s') % permission)
60 return
58 return
61
59
62 if req.method != 'POST':
60 if req.method != 'POST':
63 res.status = b'405 Method Not Allowed'
61 res.status = b'405 Method Not Allowed'
64 res.headers[b'Allow'] = b'POST'
62 res.headers[b'Allow'] = b'POST'
65 res.setbodybytes(_('commands require POST requests'))
63 res.setbodybytes(_('commands require POST requests'))
66 return
64 return
67
65
68 # At some point we'll want to use our own API instead of recycling the
66 # At some point we'll want to use our own API instead of recycling the
69 # behavior of version 1 of the wire protocol...
67 # behavior of version 1 of the wire protocol...
70 # TODO return reasonable responses - not responses that overload the
68 # TODO return reasonable responses - not responses that overload the
71 # HTTP status line message for error reporting.
69 # HTTP status line message for error reporting.
72 try:
70 try:
73 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
71 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
74 except hgwebcommon.ErrorResponse as e:
72 except hgwebcommon.ErrorResponse as e:
75 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
73 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
76 for k, v in e.headers:
74 for k, v in e.headers:
77 res.headers[k] = v
75 res.headers[k] = v
78 res.setbodybytes('permission denied')
76 res.setbodybytes('permission denied')
79 return
77 return
80
78
81 # We have a special endpoint to reflect the request back at the client.
79 # We have a special endpoint to reflect the request back at the client.
82 if command == b'debugreflect':
80 if command == b'debugreflect':
83 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
81 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
84 return
82 return
85
83
86 # Extra commands that we handle that aren't really wire protocol
84 # Extra commands that we handle that aren't really wire protocol
87 # commands. Think extra hard before making this hackery available to
85 # commands. Think extra hard before making this hackery available to
88 # extension.
86 # extension.
89 extracommands = {'multirequest'}
87 extracommands = {'multirequest'}
90
88
91 if command not in COMMANDS and command not in extracommands:
89 if command not in COMMANDS and command not in extracommands:
92 res.status = b'404 Not Found'
90 res.status = b'404 Not Found'
93 res.headers[b'Content-Type'] = b'text/plain'
91 res.headers[b'Content-Type'] = b'text/plain'
94 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
92 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
95 return
93 return
96
94
97 repo = rctx.repo
95 repo = rctx.repo
98 ui = repo.ui
96 ui = repo.ui
99
97
100 proto = httpv2protocolhandler(req, ui)
98 proto = httpv2protocolhandler(req, ui)
101
99
102 if (not COMMANDS.commandavailable(command, proto)
100 if (not COMMANDS.commandavailable(command, proto)
103 and command not in extracommands):
101 and command not in extracommands):
104 res.status = b'404 Not Found'
102 res.status = b'404 Not Found'
105 res.headers[b'Content-Type'] = b'text/plain'
103 res.headers[b'Content-Type'] = b'text/plain'
106 res.setbodybytes(_('invalid wire protocol command: %s') % command)
104 res.setbodybytes(_('invalid wire protocol command: %s') % command)
107 return
105 return
108
106
109 # TODO consider cases where proxies may add additional Accept headers.
107 # TODO consider cases where proxies may add additional Accept headers.
110 if req.headers.get(b'Accept') != FRAMINGTYPE:
108 if req.headers.get(b'Accept') != FRAMINGTYPE:
111 res.status = b'406 Not Acceptable'
109 res.status = b'406 Not Acceptable'
112 res.headers[b'Content-Type'] = b'text/plain'
110 res.headers[b'Content-Type'] = b'text/plain'
113 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
111 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
114 % FRAMINGTYPE)
112 % FRAMINGTYPE)
115 return
113 return
116
114
117 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
115 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
118 res.status = b'415 Unsupported Media Type'
116 res.status = b'415 Unsupported Media Type'
119 # TODO we should send a response with appropriate media type,
117 # TODO we should send a response with appropriate media type,
120 # since client does Accept it.
118 # since client does Accept it.
121 res.headers[b'Content-Type'] = b'text/plain'
119 res.headers[b'Content-Type'] = b'text/plain'
122 res.setbodybytes(_('client MUST send Content-Type header with '
120 res.setbodybytes(_('client MUST send Content-Type header with '
123 'value: %s\n') % FRAMINGTYPE)
121 'value: %s\n') % FRAMINGTYPE)
124 return
122 return
125
123
126 _processhttpv2request(ui, repo, req, res, permission, command, proto)
124 _processhttpv2request(ui, repo, req, res, permission, command, proto)
127
125
128 def _processhttpv2reflectrequest(ui, repo, req, res):
126 def _processhttpv2reflectrequest(ui, repo, req, res):
129 """Reads unified frame protocol request and dumps out state to client.
127 """Reads unified frame protocol request and dumps out state to client.
130
128
131 This special endpoint can be used to help debug the wire protocol.
129 This special endpoint can be used to help debug the wire protocol.
132
130
133 Instead of routing the request through the normal dispatch mechanism,
131 Instead of routing the request through the normal dispatch mechanism,
134 we instead read all frames, decode them, and feed them into our state
132 we instead read all frames, decode them, and feed them into our state
135 tracker. We then dump the log of all that activity back out to the
133 tracker. We then dump the log of all that activity back out to the
136 client.
134 client.
137 """
135 """
138 import json
136 import json
139
137
140 # Reflection APIs have a history of being abused, accidentally disclosing
138 # Reflection APIs have a history of being abused, accidentally disclosing
141 # sensitive data, etc. So we have a config knob.
139 # sensitive data, etc. So we have a config knob.
142 if not ui.configbool('experimental', 'web.api.debugreflect'):
140 if not ui.configbool('experimental', 'web.api.debugreflect'):
143 res.status = b'404 Not Found'
141 res.status = b'404 Not Found'
144 res.headers[b'Content-Type'] = b'text/plain'
142 res.headers[b'Content-Type'] = b'text/plain'
145 res.setbodybytes(_('debugreflect service not available'))
143 res.setbodybytes(_('debugreflect service not available'))
146 return
144 return
147
145
148 # We assume we have a unified framing protocol request body.
146 # We assume we have a unified framing protocol request body.
149
147
150 reactor = wireprotoframing.serverreactor()
148 reactor = wireprotoframing.serverreactor()
151 states = []
149 states = []
152
150
153 while True:
151 while True:
154 frame = wireprotoframing.readframe(req.bodyfh)
152 frame = wireprotoframing.readframe(req.bodyfh)
155
153
156 if not frame:
154 if not frame:
157 states.append(b'received: <no frame>')
155 states.append(b'received: <no frame>')
158 break
156 break
159
157
160 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
158 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
161 frame.requestid,
159 frame.requestid,
162 frame.payload))
160 frame.payload))
163
161
164 action, meta = reactor.onframerecv(frame)
162 action, meta = reactor.onframerecv(frame)
165 states.append(json.dumps((action, meta), sort_keys=True,
163 states.append(json.dumps((action, meta), sort_keys=True,
166 separators=(', ', ': ')))
164 separators=(', ', ': ')))
167
165
168 action, meta = reactor.oninputeof()
166 action, meta = reactor.oninputeof()
169 meta['action'] = action
167 meta['action'] = action
170 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
168 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
171
169
172 res.status = b'200 OK'
170 res.status = b'200 OK'
173 res.headers[b'Content-Type'] = b'text/plain'
171 res.headers[b'Content-Type'] = b'text/plain'
174 res.setbodybytes(b'\n'.join(states))
172 res.setbodybytes(b'\n'.join(states))
175
173
176 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
174 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
177 """Post-validation handler for HTTPv2 requests.
175 """Post-validation handler for HTTPv2 requests.
178
176
179 Called when the HTTP request contains unified frame-based protocol
177 Called when the HTTP request contains unified frame-based protocol
180 frames for evaluation.
178 frames for evaluation.
181 """
179 """
182 # TODO Some HTTP clients are full duplex and can receive data before
180 # TODO Some HTTP clients are full duplex and can receive data before
183 # the entire request is transmitted. Figure out a way to indicate support
181 # the entire request is transmitted. Figure out a way to indicate support
184 # for that so we can opt into full duplex mode.
182 # for that so we can opt into full duplex mode.
185 reactor = wireprotoframing.serverreactor(deferoutput=True)
183 reactor = wireprotoframing.serverreactor(deferoutput=True)
186 seencommand = False
184 seencommand = False
187
185
188 outstream = reactor.makeoutputstream()
186 outstream = reactor.makeoutputstream()
189
187
190 while True:
188 while True:
191 frame = wireprotoframing.readframe(req.bodyfh)
189 frame = wireprotoframing.readframe(req.bodyfh)
192 if not frame:
190 if not frame:
193 break
191 break
194
192
195 action, meta = reactor.onframerecv(frame)
193 action, meta = reactor.onframerecv(frame)
196
194
197 if action == 'wantframe':
195 if action == 'wantframe':
198 # Need more data before we can do anything.
196 # Need more data before we can do anything.
199 continue
197 continue
200 elif action == 'runcommand':
198 elif action == 'runcommand':
201 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
199 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
202 reqcommand, reactor, outstream,
200 reqcommand, reactor, outstream,
203 meta, issubsequent=seencommand)
201 meta, issubsequent=seencommand)
204
202
205 if sentoutput:
203 if sentoutput:
206 return
204 return
207
205
208 seencommand = True
206 seencommand = True
209
207
210 elif action == 'error':
208 elif action == 'error':
211 # TODO define proper error mechanism.
209 # TODO define proper error mechanism.
212 res.status = b'200 OK'
210 res.status = b'200 OK'
213 res.headers[b'Content-Type'] = b'text/plain'
211 res.headers[b'Content-Type'] = b'text/plain'
214 res.setbodybytes(meta['message'] + b'\n')
212 res.setbodybytes(meta['message'] + b'\n')
215 return
213 return
216 else:
214 else:
217 raise error.ProgrammingError(
215 raise error.ProgrammingError(
218 'unhandled action from frame processor: %s' % action)
216 'unhandled action from frame processor: %s' % action)
219
217
220 action, meta = reactor.oninputeof()
218 action, meta = reactor.oninputeof()
221 if action == 'sendframes':
219 if action == 'sendframes':
222 # We assume we haven't started sending the response yet. If we're
220 # We assume we haven't started sending the response yet. If we're
223 # wrong, the response type will raise an exception.
221 # wrong, the response type will raise an exception.
224 res.status = b'200 OK'
222 res.status = b'200 OK'
225 res.headers[b'Content-Type'] = FRAMINGTYPE
223 res.headers[b'Content-Type'] = FRAMINGTYPE
226 res.setbodygen(meta['framegen'])
224 res.setbodygen(meta['framegen'])
227 elif action == 'noop':
225 elif action == 'noop':
228 pass
226 pass
229 else:
227 else:
230 raise error.ProgrammingError('unhandled action from frame processor: %s'
228 raise error.ProgrammingError('unhandled action from frame processor: %s'
231 % action)
229 % action)
232
230
233 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
231 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
234 outstream, command, issubsequent):
232 outstream, command, issubsequent):
235 """Dispatch a wire protocol command made from HTTPv2 requests.
233 """Dispatch a wire protocol command made from HTTPv2 requests.
236
234
237 The authenticated permission (``authedperm``) along with the original
235 The authenticated permission (``authedperm``) along with the original
238 command from the URL (``reqcommand``) are passed in.
236 command from the URL (``reqcommand``) are passed in.
239 """
237 """
240 # We already validated that the session has permissions to perform the
238 # We already validated that the session has permissions to perform the
241 # actions in ``authedperm``. In the unified frame protocol, the canonical
239 # actions in ``authedperm``. In the unified frame protocol, the canonical
242 # command to run is expressed in a frame. However, the URL also requested
240 # command to run is expressed in a frame. However, the URL also requested
243 # to run a specific command. We need to be careful that the command we
241 # to run a specific command. We need to be careful that the command we
244 # run doesn't have permissions requirements greater than what was granted
242 # run doesn't have permissions requirements greater than what was granted
245 # by ``authedperm``.
243 # by ``authedperm``.
246 #
244 #
247 # Our rule for this is we only allow one command per HTTP request and
245 # Our rule for this is we only allow one command per HTTP request and
248 # that command must match the command in the URL. However, we make
246 # that command must match the command in the URL. However, we make
249 # an exception for the ``multirequest`` URL. This URL is allowed to
247 # an exception for the ``multirequest`` URL. This URL is allowed to
250 # execute multiple commands. We double check permissions of each command
248 # execute multiple commands. We double check permissions of each command
251 # as it is invoked to ensure there is no privilege escalation.
249 # as it is invoked to ensure there is no privilege escalation.
252 # TODO consider allowing multiple commands to regular command URLs
250 # TODO consider allowing multiple commands to regular command URLs
253 # iff each command is the same.
251 # iff each command is the same.
254
252
255 proto = httpv2protocolhandler(req, ui, args=command['args'])
253 proto = httpv2protocolhandler(req, ui, args=command['args'])
256
254
257 if reqcommand == b'multirequest':
255 if reqcommand == b'multirequest':
258 if not COMMANDS.commandavailable(command['command'], proto):
256 if not COMMANDS.commandavailable(command['command'], proto):
259 # TODO proper error mechanism
257 # TODO proper error mechanism
260 res.status = b'200 OK'
258 res.status = b'200 OK'
261 res.headers[b'Content-Type'] = b'text/plain'
259 res.headers[b'Content-Type'] = b'text/plain'
262 res.setbodybytes(_('wire protocol command not available: %s') %
260 res.setbodybytes(_('wire protocol command not available: %s') %
263 command['command'])
261 command['command'])
264 return True
262 return True
265
263
266 # TODO don't use assert here, since it may be elided by -O.
264 # TODO don't use assert here, since it may be elided by -O.
267 assert authedperm in (b'ro', b'rw')
265 assert authedperm in (b'ro', b'rw')
268 wirecommand = COMMANDS[command['command']]
266 wirecommand = COMMANDS[command['command']]
269 assert wirecommand.permission in ('push', 'pull')
267 assert wirecommand.permission in ('push', 'pull')
270
268
271 if authedperm == b'ro' and wirecommand.permission != 'pull':
269 if authedperm == b'ro' and wirecommand.permission != 'pull':
272 # TODO proper error mechanism
270 # TODO proper error mechanism
273 res.status = b'403 Forbidden'
271 res.status = b'403 Forbidden'
274 res.headers[b'Content-Type'] = b'text/plain'
272 res.headers[b'Content-Type'] = b'text/plain'
275 res.setbodybytes(_('insufficient permissions to execute '
273 res.setbodybytes(_('insufficient permissions to execute '
276 'command: %s') % command['command'])
274 'command: %s') % command['command'])
277 return True
275 return True
278
276
279 # TODO should we also call checkperm() here? Maybe not if we're going
277 # TODO should we also call checkperm() here? Maybe not if we're going
280 # to overhaul that API. The granted scope from the URL check should
278 # to overhaul that API. The granted scope from the URL check should
281 # be good enough.
279 # be good enough.
282
280
283 else:
281 else:
284 # Don't allow multiple commands outside of ``multirequest`` URL.
282 # Don't allow multiple commands outside of ``multirequest`` URL.
285 if issubsequent:
283 if issubsequent:
286 # TODO proper error mechanism
284 # TODO proper error mechanism
287 res.status = b'200 OK'
285 res.status = b'200 OK'
288 res.headers[b'Content-Type'] = b'text/plain'
286 res.headers[b'Content-Type'] = b'text/plain'
289 res.setbodybytes(_('multiple commands cannot be issued to this '
287 res.setbodybytes(_('multiple commands cannot be issued to this '
290 'URL'))
288 'URL'))
291 return True
289 return True
292
290
293 if reqcommand != command['command']:
291 if reqcommand != command['command']:
294 # TODO define proper error mechanism
292 # TODO define proper error mechanism
295 res.status = b'200 OK'
293 res.status = b'200 OK'
296 res.headers[b'Content-Type'] = b'text/plain'
294 res.headers[b'Content-Type'] = b'text/plain'
297 res.setbodybytes(_('command in frame must match command in URL'))
295 res.setbodybytes(_('command in frame must match command in URL'))
298 return True
296 return True
299
297
300 rsp = dispatch(repo, proto, command['command'])
298 rsp = dispatch(repo, proto, command['command'])
301
299
302 res.status = b'200 OK'
300 res.status = b'200 OK'
303 res.headers[b'Content-Type'] = FRAMINGTYPE
301 res.headers[b'Content-Type'] = FRAMINGTYPE
304
302
303 # TODO consider adding a type to represent an iterable of values to
304 # be CBOR encoded.
305 if isinstance(rsp, wireprototypes.cborresponse):
305 if isinstance(rsp, wireprototypes.cborresponse):
306 encoded = cbor.dumps(rsp.value, canonical=True)
306 # TODO consider calling oncommandresponsereadygen().
307 encoded = b''.join(cborutil.streamencode(rsp.value))
307 action, meta = reactor.oncommandresponseready(outstream,
308 action, meta = reactor.oncommandresponseready(outstream,
308 command['requestid'],
309 command['requestid'],
309 encoded)
310 encoded)
310 elif isinstance(rsp, wireprototypes.v2streamingresponse):
311 elif isinstance(rsp, wireprototypes.v2streamingresponse):
311 action, meta = reactor.oncommandresponsereadygen(outstream,
312 action, meta = reactor.oncommandresponsereadygen(outstream,
312 command['requestid'],
313 command['requestid'],
313 rsp.gen)
314 rsp.gen)
314 elif isinstance(rsp, wireprototypes.v2errorresponse):
315 elif isinstance(rsp, wireprototypes.v2errorresponse):
315 action, meta = reactor.oncommanderror(outstream,
316 action, meta = reactor.oncommanderror(outstream,
316 command['requestid'],
317 command['requestid'],
317 rsp.message,
318 rsp.message,
318 rsp.args)
319 rsp.args)
319 else:
320 else:
320 action, meta = reactor.onservererror(
321 action, meta = reactor.onservererror(
321 _('unhandled response type from wire proto command'))
322 _('unhandled response type from wire proto command'))
322
323
323 if action == 'sendframes':
324 if action == 'sendframes':
324 res.setbodygen(meta['framegen'])
325 res.setbodygen(meta['framegen'])
325 return True
326 return True
326 elif action == 'noop':
327 elif action == 'noop':
327 return False
328 return False
328 else:
329 else:
329 raise error.ProgrammingError('unhandled event from reactor: %s' %
330 raise error.ProgrammingError('unhandled event from reactor: %s' %
330 action)
331 action)
331
332
332 def getdispatchrepo(repo, proto, command):
333 def getdispatchrepo(repo, proto, command):
333 return repo.filtered('served')
334 return repo.filtered('served')
334
335
335 def dispatch(repo, proto, command):
336 def dispatch(repo, proto, command):
336 repo = getdispatchrepo(repo, proto, command)
337 repo = getdispatchrepo(repo, proto, command)
337
338
338 func, spec = COMMANDS[command]
339 func, spec = COMMANDS[command]
339 args = proto.getargs(spec)
340 args = proto.getargs(spec)
340
341
341 return func(repo, proto, **args)
342 return func(repo, proto, **args)
342
343
343 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
344 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
344 class httpv2protocolhandler(object):
345 class httpv2protocolhandler(object):
345 def __init__(self, req, ui, args=None):
346 def __init__(self, req, ui, args=None):
346 self._req = req
347 self._req = req
347 self._ui = ui
348 self._ui = ui
348 self._args = args
349 self._args = args
349
350
350 @property
351 @property
351 def name(self):
352 def name(self):
352 return HTTP_WIREPROTO_V2
353 return HTTP_WIREPROTO_V2
353
354
354 def getargs(self, args):
355 def getargs(self, args):
355 data = {}
356 data = {}
356 for k, typ in args.items():
357 for k, typ in args.items():
357 if k == '*':
358 if k == '*':
358 raise NotImplementedError('do not support * args')
359 raise NotImplementedError('do not support * args')
359 elif k in self._args:
360 elif k in self._args:
360 # TODO consider validating value types.
361 # TODO consider validating value types.
361 data[k] = self._args[k]
362 data[k] = self._args[k]
362
363
363 return data
364 return data
364
365
365 def getprotocaps(self):
366 def getprotocaps(self):
366 # Protocol capabilities are currently not implemented for HTTP V2.
367 # Protocol capabilities are currently not implemented for HTTP V2.
367 return set()
368 return set()
368
369
369 def getpayload(self):
370 def getpayload(self):
370 raise NotImplementedError
371 raise NotImplementedError
371
372
372 @contextlib.contextmanager
373 @contextlib.contextmanager
373 def mayberedirectstdio(self):
374 def mayberedirectstdio(self):
374 raise NotImplementedError
375 raise NotImplementedError
375
376
376 def client(self):
377 def client(self):
377 raise NotImplementedError
378 raise NotImplementedError
378
379
379 def addcapabilities(self, repo, caps):
380 def addcapabilities(self, repo, caps):
380 return caps
381 return caps
381
382
382 def checkperm(self, perm):
383 def checkperm(self, perm):
383 raise NotImplementedError
384 raise NotImplementedError
384
385
385 def httpv2apidescriptor(req, repo):
386 def httpv2apidescriptor(req, repo):
386 proto = httpv2protocolhandler(req, repo.ui)
387 proto = httpv2protocolhandler(req, repo.ui)
387
388
388 return _capabilitiesv2(repo, proto)
389 return _capabilitiesv2(repo, proto)
389
390
390 def _capabilitiesv2(repo, proto):
391 def _capabilitiesv2(repo, proto):
391 """Obtain the set of capabilities for version 2 transports.
392 """Obtain the set of capabilities for version 2 transports.
392
393
393 These capabilities are distinct from the capabilities for version 1
394 These capabilities are distinct from the capabilities for version 1
394 transports.
395 transports.
395 """
396 """
396 compression = []
397 compression = []
397 for engine in wireprototypes.supportedcompengines(repo.ui, util.SERVERROLE):
398 for engine in wireprototypes.supportedcompengines(repo.ui, util.SERVERROLE):
398 compression.append({
399 compression.append({
399 b'name': engine.wireprotosupport().name,
400 b'name': engine.wireprotosupport().name,
400 })
401 })
401
402
402 caps = {
403 caps = {
403 'commands': {},
404 'commands': {},
404 'compression': compression,
405 'compression': compression,
405 'framingmediatypes': [FRAMINGTYPE],
406 'framingmediatypes': [FRAMINGTYPE],
406 }
407 }
407
408
408 for command, entry in COMMANDS.items():
409 for command, entry in COMMANDS.items():
409 caps['commands'][command] = {
410 caps['commands'][command] = {
410 'args': entry.args,
411 'args': entry.args,
411 'permissions': [entry.permission],
412 'permissions': [entry.permission],
412 }
413 }
413
414
414 if streamclone.allowservergeneration(repo):
415 if streamclone.allowservergeneration(repo):
415 caps['rawrepoformats'] = sorted(repo.requirements &
416 caps['rawrepoformats'] = sorted(repo.requirements &
416 repo.supportedformats)
417 repo.supportedformats)
417
418
418 return proto.addcapabilities(repo, caps)
419 return proto.addcapabilities(repo, caps)
419
420
420 def wireprotocommand(name, args=None, permission='push'):
421 def wireprotocommand(name, args=None, permission='push'):
421 """Decorator to declare a wire protocol command.
422 """Decorator to declare a wire protocol command.
422
423
423 ``name`` is the name of the wire protocol command being provided.
424 ``name`` is the name of the wire protocol command being provided.
424
425
425 ``args`` is a dict of argument names to example values.
426 ``args`` is a dict of argument names to example values.
426
427
427 ``permission`` defines the permission type needed to run this command.
428 ``permission`` defines the permission type needed to run this command.
428 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
429 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
429 respectively. Default is to assume command requires ``push`` permissions
430 respectively. Default is to assume command requires ``push`` permissions
430 because otherwise commands not declaring their permissions could modify
431 because otherwise commands not declaring their permissions could modify
431 a repository that is supposed to be read-only.
432 a repository that is supposed to be read-only.
432 """
433 """
433 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
434 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
434 if v['version'] == 2}
435 if v['version'] == 2}
435
436
436 if permission not in ('push', 'pull'):
437 if permission not in ('push', 'pull'):
437 raise error.ProgrammingError('invalid wire protocol permission; '
438 raise error.ProgrammingError('invalid wire protocol permission; '
438 'got %s; expected "push" or "pull"' %
439 'got %s; expected "push" or "pull"' %
439 permission)
440 permission)
440
441
441 if args is None:
442 if args is None:
442 args = {}
443 args = {}
443
444
444 if not isinstance(args, dict):
445 if not isinstance(args, dict):
445 raise error.ProgrammingError('arguments for version 2 commands '
446 raise error.ProgrammingError('arguments for version 2 commands '
446 'must be declared as dicts')
447 'must be declared as dicts')
447
448
448 def register(func):
449 def register(func):
449 if name in COMMANDS:
450 if name in COMMANDS:
450 raise error.ProgrammingError('%s command already registered '
451 raise error.ProgrammingError('%s command already registered '
451 'for version 2' % name)
452 'for version 2' % name)
452
453
453 COMMANDS[name] = wireprototypes.commandentry(
454 COMMANDS[name] = wireprototypes.commandentry(
454 func, args=args, transports=transports, permission=permission)
455 func, args=args, transports=transports, permission=permission)
455
456
456 return func
457 return func
457
458
458 return register
459 return register
459
460
460 @wireprotocommand('branchmap', permission='pull')
461 @wireprotocommand('branchmap', permission='pull')
461 def branchmapv2(repo, proto):
462 def branchmapv2(repo, proto):
462 branchmap = {encoding.fromlocal(k): v
463 branchmap = {encoding.fromlocal(k): v
463 for k, v in repo.branchmap().iteritems()}
464 for k, v in repo.branchmap().iteritems()}
464
465
465 return wireprototypes.cborresponse(branchmap)
466 return wireprototypes.cborresponse(branchmap)
466
467
467 @wireprotocommand('capabilities', permission='pull')
468 @wireprotocommand('capabilities', permission='pull')
468 def capabilitiesv2(repo, proto):
469 def capabilitiesv2(repo, proto):
469 caps = _capabilitiesv2(repo, proto)
470 caps = _capabilitiesv2(repo, proto)
470
471
471 return wireprototypes.cborresponse(caps)
472 return wireprototypes.cborresponse(caps)
472
473
473 @wireprotocommand('heads',
474 @wireprotocommand('heads',
474 args={
475 args={
475 'publiconly': False,
476 'publiconly': False,
476 },
477 },
477 permission='pull')
478 permission='pull')
478 def headsv2(repo, proto, publiconly=False):
479 def headsv2(repo, proto, publiconly=False):
479 if publiconly:
480 if publiconly:
480 repo = repo.filtered('immutable')
481 repo = repo.filtered('immutable')
481
482
482 return wireprototypes.cborresponse(repo.heads())
483 return wireprototypes.cborresponse(repo.heads())
483
484
484 @wireprotocommand('known',
485 @wireprotocommand('known',
485 args={
486 args={
486 'nodes': [b'deadbeef'],
487 'nodes': [b'deadbeef'],
487 },
488 },
488 permission='pull')
489 permission='pull')
489 def knownv2(repo, proto, nodes=None):
490 def knownv2(repo, proto, nodes=None):
490 nodes = nodes or []
491 nodes = nodes or []
491 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
492 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
492 return wireprototypes.cborresponse(result)
493 return wireprototypes.cborresponse(result)
493
494
494 @wireprotocommand('listkeys',
495 @wireprotocommand('listkeys',
495 args={
496 args={
496 'namespace': b'ns',
497 'namespace': b'ns',
497 },
498 },
498 permission='pull')
499 permission='pull')
499 def listkeysv2(repo, proto, namespace=None):
500 def listkeysv2(repo, proto, namespace=None):
500 keys = repo.listkeys(encoding.tolocal(namespace))
501 keys = repo.listkeys(encoding.tolocal(namespace))
501 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
502 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
502 for k, v in keys.iteritems()}
503 for k, v in keys.iteritems()}
503
504
504 return wireprototypes.cborresponse(keys)
505 return wireprototypes.cborresponse(keys)
505
506
506 @wireprotocommand('lookup',
507 @wireprotocommand('lookup',
507 args={
508 args={
508 'key': b'foo',
509 'key': b'foo',
509 },
510 },
510 permission='pull')
511 permission='pull')
511 def lookupv2(repo, proto, key):
512 def lookupv2(repo, proto, key):
512 key = encoding.tolocal(key)
513 key = encoding.tolocal(key)
513
514
514 # TODO handle exception.
515 # TODO handle exception.
515 node = repo.lookup(key)
516 node = repo.lookup(key)
516
517
517 return wireprototypes.cborresponse(node)
518 return wireprototypes.cborresponse(node)
518
519
519 @wireprotocommand('pushkey',
520 @wireprotocommand('pushkey',
520 args={
521 args={
521 'namespace': b'ns',
522 'namespace': b'ns',
522 'key': b'key',
523 'key': b'key',
523 'old': b'old',
524 'old': b'old',
524 'new': b'new',
525 'new': b'new',
525 },
526 },
526 permission='push')
527 permission='push')
527 def pushkeyv2(repo, proto, namespace, key, old, new):
528 def pushkeyv2(repo, proto, namespace, key, old, new):
528 # TODO handle ui output redirection
529 # TODO handle ui output redirection
529 r = repo.pushkey(encoding.tolocal(namespace),
530 r = repo.pushkey(encoding.tolocal(namespace),
530 encoding.tolocal(key),
531 encoding.tolocal(key),
531 encoding.tolocal(old),
532 encoding.tolocal(old),
532 encoding.tolocal(new))
533 encoding.tolocal(new))
533
534
534 return wireprototypes.cborresponse(r)
535 return wireprototypes.cborresponse(r)
@@ -1,571 +1,571 b''
1 #require no-chg
1 #require no-chg
2
2
3 $ . $TESTDIR/wireprotohelpers.sh
3 $ . $TESTDIR/wireprotohelpers.sh
4 $ enabledummycommands
4 $ enabledummycommands
5
5
6 $ hg init server
6 $ hg init server
7 $ cat > server/.hg/hgrc << EOF
7 $ cat > server/.hg/hgrc << EOF
8 > [experimental]
8 > [experimental]
9 > web.apiserver = true
9 > web.apiserver = true
10 > EOF
10 > EOF
11 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
11 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
12 $ cat hg.pid > $DAEMON_PIDS
12 $ cat hg.pid > $DAEMON_PIDS
13
13
14 HTTP v2 protocol not enabled by default
14 HTTP v2 protocol not enabled by default
15
15
16 $ sendhttpraw << EOF
16 $ sendhttpraw << EOF
17 > httprequest GET api/$HTTPV2
17 > httprequest GET api/$HTTPV2
18 > user-agent: test
18 > user-agent: test
19 > EOF
19 > EOF
20 using raw connection to peer
20 using raw connection to peer
21 s> GET /api/exp-http-v2-0001 HTTP/1.1\r\n
21 s> GET /api/exp-http-v2-0001 HTTP/1.1\r\n
22 s> Accept-Encoding: identity\r\n
22 s> Accept-Encoding: identity\r\n
23 s> user-agent: test\r\n
23 s> user-agent: test\r\n
24 s> host: $LOCALIP:$HGPORT\r\n (glob)
24 s> host: $LOCALIP:$HGPORT\r\n (glob)
25 s> \r\n
25 s> \r\n
26 s> makefile('rb', None)
26 s> makefile('rb', None)
27 s> HTTP/1.1 404 Not Found\r\n
27 s> HTTP/1.1 404 Not Found\r\n
28 s> Server: testing stub value\r\n
28 s> Server: testing stub value\r\n
29 s> Date: $HTTP_DATE$\r\n
29 s> Date: $HTTP_DATE$\r\n
30 s> Content-Type: text/plain\r\n
30 s> Content-Type: text/plain\r\n
31 s> Content-Length: 33\r\n
31 s> Content-Length: 33\r\n
32 s> \r\n
32 s> \r\n
33 s> API exp-http-v2-0001 not enabled\n
33 s> API exp-http-v2-0001 not enabled\n
34
34
35 Restart server with support for HTTP v2 API
35 Restart server with support for HTTP v2 API
36
36
37 $ killdaemons.py
37 $ killdaemons.py
38 $ enablehttpv2 server
38 $ enablehttpv2 server
39 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
39 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
40 $ cat hg.pid > $DAEMON_PIDS
40 $ cat hg.pid > $DAEMON_PIDS
41
41
42 Request to unknown command yields 404
42 Request to unknown command yields 404
43
43
44 $ sendhttpraw << EOF
44 $ sendhttpraw << EOF
45 > httprequest POST api/$HTTPV2/ro/badcommand
45 > httprequest POST api/$HTTPV2/ro/badcommand
46 > user-agent: test
46 > user-agent: test
47 > EOF
47 > EOF
48 using raw connection to peer
48 using raw connection to peer
49 s> POST /api/exp-http-v2-0001/ro/badcommand HTTP/1.1\r\n
49 s> POST /api/exp-http-v2-0001/ro/badcommand HTTP/1.1\r\n
50 s> Accept-Encoding: identity\r\n
50 s> Accept-Encoding: identity\r\n
51 s> user-agent: test\r\n
51 s> user-agent: test\r\n
52 s> host: $LOCALIP:$HGPORT\r\n (glob)
52 s> host: $LOCALIP:$HGPORT\r\n (glob)
53 s> \r\n
53 s> \r\n
54 s> makefile('rb', None)
54 s> makefile('rb', None)
55 s> HTTP/1.1 404 Not Found\r\n
55 s> HTTP/1.1 404 Not Found\r\n
56 s> Server: testing stub value\r\n
56 s> Server: testing stub value\r\n
57 s> Date: $HTTP_DATE$\r\n
57 s> Date: $HTTP_DATE$\r\n
58 s> Content-Type: text/plain\r\n
58 s> Content-Type: text/plain\r\n
59 s> Content-Length: 42\r\n
59 s> Content-Length: 42\r\n
60 s> \r\n
60 s> \r\n
61 s> unknown wire protocol command: badcommand\n
61 s> unknown wire protocol command: badcommand\n
62
62
63 GET to read-only command yields a 405
63 GET to read-only command yields a 405
64
64
65 $ sendhttpraw << EOF
65 $ sendhttpraw << EOF
66 > httprequest GET api/$HTTPV2/ro/customreadonly
66 > httprequest GET api/$HTTPV2/ro/customreadonly
67 > user-agent: test
67 > user-agent: test
68 > EOF
68 > EOF
69 using raw connection to peer
69 using raw connection to peer
70 s> GET /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
70 s> GET /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
71 s> Accept-Encoding: identity\r\n
71 s> Accept-Encoding: identity\r\n
72 s> user-agent: test\r\n
72 s> user-agent: test\r\n
73 s> host: $LOCALIP:$HGPORT\r\n (glob)
73 s> host: $LOCALIP:$HGPORT\r\n (glob)
74 s> \r\n
74 s> \r\n
75 s> makefile('rb', None)
75 s> makefile('rb', None)
76 s> HTTP/1.1 405 Method Not Allowed\r\n
76 s> HTTP/1.1 405 Method Not Allowed\r\n
77 s> Server: testing stub value\r\n
77 s> Server: testing stub value\r\n
78 s> Date: $HTTP_DATE$\r\n
78 s> Date: $HTTP_DATE$\r\n
79 s> Allow: POST\r\n
79 s> Allow: POST\r\n
80 s> Content-Length: 30\r\n
80 s> Content-Length: 30\r\n
81 s> \r\n
81 s> \r\n
82 s> commands require POST requests
82 s> commands require POST requests
83
83
84 Missing Accept header results in 406
84 Missing Accept header results in 406
85
85
86 $ sendhttpraw << EOF
86 $ sendhttpraw << EOF
87 > httprequest POST api/$HTTPV2/ro/customreadonly
87 > httprequest POST api/$HTTPV2/ro/customreadonly
88 > user-agent: test
88 > user-agent: test
89 > EOF
89 > EOF
90 using raw connection to peer
90 using raw connection to peer
91 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
91 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
92 s> Accept-Encoding: identity\r\n
92 s> Accept-Encoding: identity\r\n
93 s> user-agent: test\r\n
93 s> user-agent: test\r\n
94 s> host: $LOCALIP:$HGPORT\r\n (glob)
94 s> host: $LOCALIP:$HGPORT\r\n (glob)
95 s> \r\n
95 s> \r\n
96 s> makefile('rb', None)
96 s> makefile('rb', None)
97 s> HTTP/1.1 406 Not Acceptable\r\n
97 s> HTTP/1.1 406 Not Acceptable\r\n
98 s> Server: testing stub value\r\n
98 s> Server: testing stub value\r\n
99 s> Date: $HTTP_DATE$\r\n
99 s> Date: $HTTP_DATE$\r\n
100 s> Content-Type: text/plain\r\n
100 s> Content-Type: text/plain\r\n
101 s> Content-Length: 85\r\n
101 s> Content-Length: 85\r\n
102 s> \r\n
102 s> \r\n
103 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0005\n
103 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0005\n
104
104
105 Bad Accept header results in 406
105 Bad Accept header results in 406
106
106
107 $ sendhttpraw << EOF
107 $ sendhttpraw << EOF
108 > httprequest POST api/$HTTPV2/ro/customreadonly
108 > httprequest POST api/$HTTPV2/ro/customreadonly
109 > accept: invalid
109 > accept: invalid
110 > user-agent: test
110 > user-agent: test
111 > EOF
111 > EOF
112 using raw connection to peer
112 using raw connection to peer
113 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
113 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
114 s> Accept-Encoding: identity\r\n
114 s> Accept-Encoding: identity\r\n
115 s> accept: invalid\r\n
115 s> accept: invalid\r\n
116 s> user-agent: test\r\n
116 s> user-agent: test\r\n
117 s> host: $LOCALIP:$HGPORT\r\n (glob)
117 s> host: $LOCALIP:$HGPORT\r\n (glob)
118 s> \r\n
118 s> \r\n
119 s> makefile('rb', None)
119 s> makefile('rb', None)
120 s> HTTP/1.1 406 Not Acceptable\r\n
120 s> HTTP/1.1 406 Not Acceptable\r\n
121 s> Server: testing stub value\r\n
121 s> Server: testing stub value\r\n
122 s> Date: $HTTP_DATE$\r\n
122 s> Date: $HTTP_DATE$\r\n
123 s> Content-Type: text/plain\r\n
123 s> Content-Type: text/plain\r\n
124 s> Content-Length: 85\r\n
124 s> Content-Length: 85\r\n
125 s> \r\n
125 s> \r\n
126 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0005\n
126 s> client MUST specify Accept header with value: application/mercurial-exp-framing-0005\n
127
127
128 Bad Content-Type header results in 415
128 Bad Content-Type header results in 415
129
129
130 $ sendhttpraw << EOF
130 $ sendhttpraw << EOF
131 > httprequest POST api/$HTTPV2/ro/customreadonly
131 > httprequest POST api/$HTTPV2/ro/customreadonly
132 > accept: $MEDIATYPE
132 > accept: $MEDIATYPE
133 > user-agent: test
133 > user-agent: test
134 > content-type: badmedia
134 > content-type: badmedia
135 > EOF
135 > EOF
136 using raw connection to peer
136 using raw connection to peer
137 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
137 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
138 s> Accept-Encoding: identity\r\n
138 s> Accept-Encoding: identity\r\n
139 s> accept: application/mercurial-exp-framing-0005\r\n
139 s> accept: application/mercurial-exp-framing-0005\r\n
140 s> content-type: badmedia\r\n
140 s> content-type: badmedia\r\n
141 s> user-agent: test\r\n
141 s> user-agent: test\r\n
142 s> host: $LOCALIP:$HGPORT\r\n (glob)
142 s> host: $LOCALIP:$HGPORT\r\n (glob)
143 s> \r\n
143 s> \r\n
144 s> makefile('rb', None)
144 s> makefile('rb', None)
145 s> HTTP/1.1 415 Unsupported Media Type\r\n
145 s> HTTP/1.1 415 Unsupported Media Type\r\n
146 s> Server: testing stub value\r\n
146 s> Server: testing stub value\r\n
147 s> Date: $HTTP_DATE$\r\n
147 s> Date: $HTTP_DATE$\r\n
148 s> Content-Type: text/plain\r\n
148 s> Content-Type: text/plain\r\n
149 s> Content-Length: 88\r\n
149 s> Content-Length: 88\r\n
150 s> \r\n
150 s> \r\n
151 s> client MUST send Content-Type header with value: application/mercurial-exp-framing-0005\n
151 s> client MUST send Content-Type header with value: application/mercurial-exp-framing-0005\n
152
152
153 Request to read-only command works out of the box
153 Request to read-only command works out of the box
154
154
155 $ sendhttpraw << EOF
155 $ sendhttpraw << EOF
156 > httprequest POST api/$HTTPV2/ro/customreadonly
156 > httprequest POST api/$HTTPV2/ro/customreadonly
157 > accept: $MEDIATYPE
157 > accept: $MEDIATYPE
158 > content-type: $MEDIATYPE
158 > content-type: $MEDIATYPE
159 > user-agent: test
159 > user-agent: test
160 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
160 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
161 > EOF
161 > EOF
162 using raw connection to peer
162 using raw connection to peer
163 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
163 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
164 s> Accept-Encoding: identity\r\n
164 s> Accept-Encoding: identity\r\n
165 s> *\r\n (glob)
165 s> *\r\n (glob)
166 s> content-type: application/mercurial-exp-framing-0005\r\n
166 s> content-type: application/mercurial-exp-framing-0005\r\n
167 s> user-agent: test\r\n
167 s> user-agent: test\r\n
168 s> content-length: 29\r\n
168 s> content-length: 29\r\n
169 s> host: $LOCALIP:$HGPORT\r\n (glob)
169 s> host: $LOCALIP:$HGPORT\r\n (glob)
170 s> \r\n
170 s> \r\n
171 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
171 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
172 s> makefile('rb', None)
172 s> makefile('rb', None)
173 s> HTTP/1.1 200 OK\r\n
173 s> HTTP/1.1 200 OK\r\n
174 s> Server: testing stub value\r\n
174 s> Server: testing stub value\r\n
175 s> Date: $HTTP_DATE$\r\n
175 s> Date: $HTTP_DATE$\r\n
176 s> Content-Type: application/mercurial-exp-framing-0005\r\n
176 s> Content-Type: application/mercurial-exp-framing-0005\r\n
177 s> Transfer-Encoding: chunked\r\n
177 s> Transfer-Encoding: chunked\r\n
178 s> \r\n
178 s> \r\n
179 s> 32\r\n
179 s> 32\r\n
180 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
180 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
181 s> \r\n
181 s> \r\n
182 s> 0\r\n
182 s> 0\r\n
183 s> \r\n
183 s> \r\n
184
184
185 $ sendhttpv2peer << EOF
185 $ sendhttpv2peer << EOF
186 > command customreadonly
186 > command customreadonly
187 > EOF
187 > EOF
188 creating http peer for wire protocol version 2
188 creating http peer for wire protocol version 2
189 sending customreadonly command
189 sending customreadonly command
190 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
190 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
191 s> Accept-Encoding: identity\r\n
191 s> Accept-Encoding: identity\r\n
192 s> accept: application/mercurial-exp-framing-0005\r\n
192 s> accept: application/mercurial-exp-framing-0005\r\n
193 s> content-type: application/mercurial-exp-framing-0005\r\n
193 s> content-type: application/mercurial-exp-framing-0005\r\n
194 s> content-length: 29\r\n
194 s> content-length: 29\r\n
195 s> host: $LOCALIP:$HGPORT\r\n (glob)
195 s> host: $LOCALIP:$HGPORT\r\n (glob)
196 s> user-agent: Mercurial debugwireproto\r\n
196 s> user-agent: Mercurial debugwireproto\r\n
197 s> \r\n
197 s> \r\n
198 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
198 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
199 s> makefile('rb', None)
199 s> makefile('rb', None)
200 s> HTTP/1.1 200 OK\r\n
200 s> HTTP/1.1 200 OK\r\n
201 s> Server: testing stub value\r\n
201 s> Server: testing stub value\r\n
202 s> Date: $HTTP_DATE$\r\n
202 s> Date: $HTTP_DATE$\r\n
203 s> Content-Type: application/mercurial-exp-framing-0005\r\n
203 s> Content-Type: application/mercurial-exp-framing-0005\r\n
204 s> Transfer-Encoding: chunked\r\n
204 s> Transfer-Encoding: chunked\r\n
205 s> \r\n
205 s> \r\n
206 s> 32\r\n
206 s> 32\r\n
207 s> *\x00\x00\x01\x00\x02\x012
207 s> *\x00\x00\x01\x00\x02\x012
208 s> \xa1FstatusBokX\x1dcustomreadonly bytes response
208 s> \xa1FstatusBokX\x1dcustomreadonly bytes response
209 s> \r\n
209 s> \r\n
210 received frame(size=42; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
210 received frame(size=42; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
211 s> 0\r\n
211 s> 0\r\n
212 s> \r\n
212 s> \r\n
213 response: [
213 response: [
214 {
214 {
215 b'status': b'ok'
215 b'status': b'ok'
216 },
216 },
217 b'customreadonly bytes response'
217 b'customreadonly bytes response'
218 ]
218 ]
219
219
220 Request to read-write command fails because server is read-only by default
220 Request to read-write command fails because server is read-only by default
221
221
222 GET to read-write request yields 405
222 GET to read-write request yields 405
223
223
224 $ sendhttpraw << EOF
224 $ sendhttpraw << EOF
225 > httprequest GET api/$HTTPV2/rw/customreadonly
225 > httprequest GET api/$HTTPV2/rw/customreadonly
226 > user-agent: test
226 > user-agent: test
227 > EOF
227 > EOF
228 using raw connection to peer
228 using raw connection to peer
229 s> GET /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
229 s> GET /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
230 s> Accept-Encoding: identity\r\n
230 s> Accept-Encoding: identity\r\n
231 s> user-agent: test\r\n
231 s> user-agent: test\r\n
232 s> host: $LOCALIP:$HGPORT\r\n (glob)
232 s> host: $LOCALIP:$HGPORT\r\n (glob)
233 s> \r\n
233 s> \r\n
234 s> makefile('rb', None)
234 s> makefile('rb', None)
235 s> HTTP/1.1 405 Method Not Allowed\r\n
235 s> HTTP/1.1 405 Method Not Allowed\r\n
236 s> Server: testing stub value\r\n
236 s> Server: testing stub value\r\n
237 s> Date: $HTTP_DATE$\r\n
237 s> Date: $HTTP_DATE$\r\n
238 s> Allow: POST\r\n
238 s> Allow: POST\r\n
239 s> Content-Length: 30\r\n
239 s> Content-Length: 30\r\n
240 s> \r\n
240 s> \r\n
241 s> commands require POST requests
241 s> commands require POST requests
242
242
243 Even for unknown commands
243 Even for unknown commands
244
244
245 $ sendhttpraw << EOF
245 $ sendhttpraw << EOF
246 > httprequest GET api/$HTTPV2/rw/badcommand
246 > httprequest GET api/$HTTPV2/rw/badcommand
247 > user-agent: test
247 > user-agent: test
248 > EOF
248 > EOF
249 using raw connection to peer
249 using raw connection to peer
250 s> GET /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
250 s> GET /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
251 s> Accept-Encoding: identity\r\n
251 s> Accept-Encoding: identity\r\n
252 s> user-agent: test\r\n
252 s> user-agent: test\r\n
253 s> host: $LOCALIP:$HGPORT\r\n (glob)
253 s> host: $LOCALIP:$HGPORT\r\n (glob)
254 s> \r\n
254 s> \r\n
255 s> makefile('rb', None)
255 s> makefile('rb', None)
256 s> HTTP/1.1 405 Method Not Allowed\r\n
256 s> HTTP/1.1 405 Method Not Allowed\r\n
257 s> Server: testing stub value\r\n
257 s> Server: testing stub value\r\n
258 s> Date: $HTTP_DATE$\r\n
258 s> Date: $HTTP_DATE$\r\n
259 s> Allow: POST\r\n
259 s> Allow: POST\r\n
260 s> Content-Length: 30\r\n
260 s> Content-Length: 30\r\n
261 s> \r\n
261 s> \r\n
262 s> commands require POST requests
262 s> commands require POST requests
263
263
264 SSL required by default
264 SSL required by default
265
265
266 $ sendhttpraw << EOF
266 $ sendhttpraw << EOF
267 > httprequest POST api/$HTTPV2/rw/customreadonly
267 > httprequest POST api/$HTTPV2/rw/customreadonly
268 > user-agent: test
268 > user-agent: test
269 > EOF
269 > EOF
270 using raw connection to peer
270 using raw connection to peer
271 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
271 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
272 s> Accept-Encoding: identity\r\n
272 s> Accept-Encoding: identity\r\n
273 s> user-agent: test\r\n
273 s> user-agent: test\r\n
274 s> host: $LOCALIP:$HGPORT\r\n (glob)
274 s> host: $LOCALIP:$HGPORT\r\n (glob)
275 s> \r\n
275 s> \r\n
276 s> makefile('rb', None)
276 s> makefile('rb', None)
277 s> HTTP/1.1 403 ssl required\r\n
277 s> HTTP/1.1 403 ssl required\r\n
278 s> Server: testing stub value\r\n
278 s> Server: testing stub value\r\n
279 s> Date: $HTTP_DATE$\r\n
279 s> Date: $HTTP_DATE$\r\n
280 s> Content-Length: 17\r\n
280 s> Content-Length: 17\r\n
281 s> \r\n
281 s> \r\n
282 s> permission denied
282 s> permission denied
283
283
284 Restart server to allow non-ssl read-write operations
284 Restart server to allow non-ssl read-write operations
285
285
286 $ killdaemons.py
286 $ killdaemons.py
287 $ cat > server/.hg/hgrc << EOF
287 $ cat > server/.hg/hgrc << EOF
288 > [experimental]
288 > [experimental]
289 > web.apiserver = true
289 > web.apiserver = true
290 > web.api.http-v2 = true
290 > web.api.http-v2 = true
291 > [web]
291 > [web]
292 > push_ssl = false
292 > push_ssl = false
293 > allow-push = *
293 > allow-push = *
294 > EOF
294 > EOF
295
295
296 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
296 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
297 $ cat hg.pid > $DAEMON_PIDS
297 $ cat hg.pid > $DAEMON_PIDS
298
298
299 Authorized request for valid read-write command works
299 Authorized request for valid read-write command works
300
300
301 $ sendhttpraw << EOF
301 $ sendhttpraw << EOF
302 > httprequest POST api/$HTTPV2/rw/customreadonly
302 > httprequest POST api/$HTTPV2/rw/customreadonly
303 > user-agent: test
303 > user-agent: test
304 > accept: $MEDIATYPE
304 > accept: $MEDIATYPE
305 > content-type: $MEDIATYPE
305 > content-type: $MEDIATYPE
306 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
306 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
307 > EOF
307 > EOF
308 using raw connection to peer
308 using raw connection to peer
309 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
309 s> POST /api/exp-http-v2-0001/rw/customreadonly HTTP/1.1\r\n
310 s> Accept-Encoding: identity\r\n
310 s> Accept-Encoding: identity\r\n
311 s> accept: application/mercurial-exp-framing-0005\r\n
311 s> accept: application/mercurial-exp-framing-0005\r\n
312 s> content-type: application/mercurial-exp-framing-0005\r\n
312 s> content-type: application/mercurial-exp-framing-0005\r\n
313 s> user-agent: test\r\n
313 s> user-agent: test\r\n
314 s> content-length: 29\r\n
314 s> content-length: 29\r\n
315 s> host: $LOCALIP:$HGPORT\r\n (glob)
315 s> host: $LOCALIP:$HGPORT\r\n (glob)
316 s> \r\n
316 s> \r\n
317 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
317 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
318 s> makefile('rb', None)
318 s> makefile('rb', None)
319 s> HTTP/1.1 200 OK\r\n
319 s> HTTP/1.1 200 OK\r\n
320 s> Server: testing stub value\r\n
320 s> Server: testing stub value\r\n
321 s> Date: $HTTP_DATE$\r\n
321 s> Date: $HTTP_DATE$\r\n
322 s> Content-Type: application/mercurial-exp-framing-0005\r\n
322 s> Content-Type: application/mercurial-exp-framing-0005\r\n
323 s> Transfer-Encoding: chunked\r\n
323 s> Transfer-Encoding: chunked\r\n
324 s> \r\n
324 s> \r\n
325 s> 32\r\n
325 s> 32\r\n
326 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
326 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
327 s> \r\n
327 s> \r\n
328 s> 0\r\n
328 s> 0\r\n
329 s> \r\n
329 s> \r\n
330
330
331 Authorized request for unknown command is rejected
331 Authorized request for unknown command is rejected
332
332
333 $ sendhttpraw << EOF
333 $ sendhttpraw << EOF
334 > httprequest POST api/$HTTPV2/rw/badcommand
334 > httprequest POST api/$HTTPV2/rw/badcommand
335 > user-agent: test
335 > user-agent: test
336 > accept: $MEDIATYPE
336 > accept: $MEDIATYPE
337 > EOF
337 > EOF
338 using raw connection to peer
338 using raw connection to peer
339 s> POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
339 s> POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
340 s> Accept-Encoding: identity\r\n
340 s> Accept-Encoding: identity\r\n
341 s> accept: application/mercurial-exp-framing-0005\r\n
341 s> accept: application/mercurial-exp-framing-0005\r\n
342 s> user-agent: test\r\n
342 s> user-agent: test\r\n
343 s> host: $LOCALIP:$HGPORT\r\n (glob)
343 s> host: $LOCALIP:$HGPORT\r\n (glob)
344 s> \r\n
344 s> \r\n
345 s> makefile('rb', None)
345 s> makefile('rb', None)
346 s> HTTP/1.1 404 Not Found\r\n
346 s> HTTP/1.1 404 Not Found\r\n
347 s> Server: testing stub value\r\n
347 s> Server: testing stub value\r\n
348 s> Date: $HTTP_DATE$\r\n
348 s> Date: $HTTP_DATE$\r\n
349 s> Content-Type: text/plain\r\n
349 s> Content-Type: text/plain\r\n
350 s> Content-Length: 42\r\n
350 s> Content-Length: 42\r\n
351 s> \r\n
351 s> \r\n
352 s> unknown wire protocol command: badcommand\n
352 s> unknown wire protocol command: badcommand\n
353
353
354 debugreflect isn't enabled by default
354 debugreflect isn't enabled by default
355
355
356 $ sendhttpraw << EOF
356 $ sendhttpraw << EOF
357 > httprequest POST api/$HTTPV2/ro/debugreflect
357 > httprequest POST api/$HTTPV2/ro/debugreflect
358 > user-agent: test
358 > user-agent: test
359 > EOF
359 > EOF
360 using raw connection to peer
360 using raw connection to peer
361 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
361 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
362 s> Accept-Encoding: identity\r\n
362 s> Accept-Encoding: identity\r\n
363 s> user-agent: test\r\n
363 s> user-agent: test\r\n
364 s> host: $LOCALIP:$HGPORT\r\n (glob)
364 s> host: $LOCALIP:$HGPORT\r\n (glob)
365 s> \r\n
365 s> \r\n
366 s> makefile('rb', None)
366 s> makefile('rb', None)
367 s> HTTP/1.1 404 Not Found\r\n
367 s> HTTP/1.1 404 Not Found\r\n
368 s> Server: testing stub value\r\n
368 s> Server: testing stub value\r\n
369 s> Date: $HTTP_DATE$\r\n
369 s> Date: $HTTP_DATE$\r\n
370 s> Content-Type: text/plain\r\n
370 s> Content-Type: text/plain\r\n
371 s> Content-Length: 34\r\n
371 s> Content-Length: 34\r\n
372 s> \r\n
372 s> \r\n
373 s> debugreflect service not available
373 s> debugreflect service not available
374
374
375 Restart server to get debugreflect endpoint
375 Restart server to get debugreflect endpoint
376
376
377 $ killdaemons.py
377 $ killdaemons.py
378 $ cat > server/.hg/hgrc << EOF
378 $ cat > server/.hg/hgrc << EOF
379 > [experimental]
379 > [experimental]
380 > web.apiserver = true
380 > web.apiserver = true
381 > web.api.debugreflect = true
381 > web.api.debugreflect = true
382 > web.api.http-v2 = true
382 > web.api.http-v2 = true
383 > [web]
383 > [web]
384 > push_ssl = false
384 > push_ssl = false
385 > allow-push = *
385 > allow-push = *
386 > EOF
386 > EOF
387
387
388 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
388 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
389 $ cat hg.pid > $DAEMON_PIDS
389 $ cat hg.pid > $DAEMON_PIDS
390
390
391 Command frames can be reflected via debugreflect
391 Command frames can be reflected via debugreflect
392
392
393 $ sendhttpraw << EOF
393 $ sendhttpraw << EOF
394 > httprequest POST api/$HTTPV2/ro/debugreflect
394 > httprequest POST api/$HTTPV2/ro/debugreflect
395 > accept: $MEDIATYPE
395 > accept: $MEDIATYPE
396 > content-type: $MEDIATYPE
396 > content-type: $MEDIATYPE
397 > user-agent: test
397 > user-agent: test
398 > frame 1 1 stream-begin command-request new cbor:{b'name': b'command1', b'args': {b'foo': b'val1', b'bar1': b'val'}}
398 > frame 1 1 stream-begin command-request new cbor:{b'name': b'command1', b'args': {b'foo': b'val1', b'bar1': b'val'}}
399 > EOF
399 > EOF
400 using raw connection to peer
400 using raw connection to peer
401 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
401 s> POST /api/exp-http-v2-0001/ro/debugreflect HTTP/1.1\r\n
402 s> Accept-Encoding: identity\r\n
402 s> Accept-Encoding: identity\r\n
403 s> accept: application/mercurial-exp-framing-0005\r\n
403 s> accept: application/mercurial-exp-framing-0005\r\n
404 s> content-type: application/mercurial-exp-framing-0005\r\n
404 s> content-type: application/mercurial-exp-framing-0005\r\n
405 s> user-agent: test\r\n
405 s> user-agent: test\r\n
406 s> content-length: 47\r\n
406 s> content-length: 47\r\n
407 s> host: $LOCALIP:$HGPORT\r\n (glob)
407 s> host: $LOCALIP:$HGPORT\r\n (glob)
408 s> \r\n
408 s> \r\n
409 s> \'\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1
409 s> \'\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1
410 s> makefile('rb', None)
410 s> makefile('rb', None)
411 s> HTTP/1.1 200 OK\r\n
411 s> HTTP/1.1 200 OK\r\n
412 s> Server: testing stub value\r\n
412 s> Server: testing stub value\r\n
413 s> Date: $HTTP_DATE$\r\n
413 s> Date: $HTTP_DATE$\r\n
414 s> Content-Type: text/plain\r\n
414 s> Content-Type: text/plain\r\n
415 s> Content-Length: 205\r\n
415 s> Content-Length: 205\r\n
416 s> \r\n
416 s> \r\n
417 s> received: 1 1 1 \xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1\n
417 s> received: 1 1 1 \xa2Dargs\xa2Dbar1CvalCfooDval1DnameHcommand1\n
418 s> ["runcommand", {"args": {"bar1": "val", "foo": "val1"}, "command": "command1", "data": null, "requestid": 1}]\n
418 s> ["runcommand", {"args": {"bar1": "val", "foo": "val1"}, "command": "command1", "data": null, "requestid": 1}]\n
419 s> received: <no frame>\n
419 s> received: <no frame>\n
420 s> {"action": "noop"}
420 s> {"action": "noop"}
421
421
422 Multiple requests to regular command URL are not allowed
422 Multiple requests to regular command URL are not allowed
423
423
424 $ sendhttpraw << EOF
424 $ sendhttpraw << EOF
425 > httprequest POST api/$HTTPV2/ro/customreadonly
425 > httprequest POST api/$HTTPV2/ro/customreadonly
426 > accept: $MEDIATYPE
426 > accept: $MEDIATYPE
427 > content-type: $MEDIATYPE
427 > content-type: $MEDIATYPE
428 > user-agent: test
428 > user-agent: test
429 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
429 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
430 > EOF
430 > EOF
431 using raw connection to peer
431 using raw connection to peer
432 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
432 s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n
433 s> Accept-Encoding: identity\r\n
433 s> Accept-Encoding: identity\r\n
434 s> accept: application/mercurial-exp-framing-0005\r\n
434 s> accept: application/mercurial-exp-framing-0005\r\n
435 s> content-type: application/mercurial-exp-framing-0005\r\n
435 s> content-type: application/mercurial-exp-framing-0005\r\n
436 s> user-agent: test\r\n
436 s> user-agent: test\r\n
437 s> content-length: 29\r\n
437 s> content-length: 29\r\n
438 s> host: $LOCALIP:$HGPORT\r\n (glob)
438 s> host: $LOCALIP:$HGPORT\r\n (glob)
439 s> \r\n
439 s> \r\n
440 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
440 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly
441 s> makefile('rb', None)
441 s> makefile('rb', None)
442 s> HTTP/1.1 200 OK\r\n
442 s> HTTP/1.1 200 OK\r\n
443 s> Server: testing stub value\r\n
443 s> Server: testing stub value\r\n
444 s> Date: $HTTP_DATE$\r\n
444 s> Date: $HTTP_DATE$\r\n
445 s> Content-Type: application/mercurial-exp-framing-0005\r\n
445 s> Content-Type: application/mercurial-exp-framing-0005\r\n
446 s> Transfer-Encoding: chunked\r\n
446 s> Transfer-Encoding: chunked\r\n
447 s> \r\n
447 s> \r\n
448 s> 32\r\n
448 s> 32\r\n
449 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
449 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
450 s> \r\n
450 s> \r\n
451 s> 0\r\n
451 s> 0\r\n
452 s> \r\n
452 s> \r\n
453
453
454 Multiple requests to "multirequest" URL are allowed
454 Multiple requests to "multirequest" URL are allowed
455
455
456 $ sendhttpraw << EOF
456 $ sendhttpraw << EOF
457 > httprequest POST api/$HTTPV2/ro/multirequest
457 > httprequest POST api/$HTTPV2/ro/multirequest
458 > accept: $MEDIATYPE
458 > accept: $MEDIATYPE
459 > content-type: $MEDIATYPE
459 > content-type: $MEDIATYPE
460 > user-agent: test
460 > user-agent: test
461 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
461 > frame 1 1 stream-begin command-request new cbor:{b'name': b'customreadonly'}
462 > frame 3 1 0 command-request new cbor:{b'name': b'customreadonly'}
462 > frame 3 1 0 command-request new cbor:{b'name': b'customreadonly'}
463 > EOF
463 > EOF
464 using raw connection to peer
464 using raw connection to peer
465 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
465 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
466 s> Accept-Encoding: identity\r\n
466 s> Accept-Encoding: identity\r\n
467 s> *\r\n (glob)
467 s> *\r\n (glob)
468 s> *\r\n (glob)
468 s> *\r\n (glob)
469 s> user-agent: test\r\n
469 s> user-agent: test\r\n
470 s> content-length: 58\r\n
470 s> content-length: 58\r\n
471 s> host: $LOCALIP:$HGPORT\r\n (glob)
471 s> host: $LOCALIP:$HGPORT\r\n (glob)
472 s> \r\n
472 s> \r\n
473 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly\x15\x00\x00\x03\x00\x01\x00\x11\xa1DnameNcustomreadonly
473 s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly\x15\x00\x00\x03\x00\x01\x00\x11\xa1DnameNcustomreadonly
474 s> makefile('rb', None)
474 s> makefile('rb', None)
475 s> HTTP/1.1 200 OK\r\n
475 s> HTTP/1.1 200 OK\r\n
476 s> Server: testing stub value\r\n
476 s> Server: testing stub value\r\n
477 s> Date: $HTTP_DATE$\r\n
477 s> Date: $HTTP_DATE$\r\n
478 s> Content-Type: application/mercurial-exp-framing-0005\r\n
478 s> Content-Type: application/mercurial-exp-framing-0005\r\n
479 s> Transfer-Encoding: chunked\r\n
479 s> Transfer-Encoding: chunked\r\n
480 s> \r\n
480 s> \r\n
481 s> 32\r\n
481 s> 32\r\n
482 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
482 s> *\x00\x00\x01\x00\x02\x012\xa1FstatusBokX\x1dcustomreadonly bytes response
483 s> \r\n
483 s> \r\n
484 s> 32\r\n
484 s> 32\r\n
485 s> *\x00\x00\x03\x00\x02\x002\xa1FstatusBokX\x1dcustomreadonly bytes response
485 s> *\x00\x00\x03\x00\x02\x002\xa1FstatusBokX\x1dcustomreadonly bytes response
486 s> \r\n
486 s> \r\n
487 s> 0\r\n
487 s> 0\r\n
488 s> \r\n
488 s> \r\n
489
489
490 Interleaved requests to "multirequest" are processed
490 Interleaved requests to "multirequest" are processed
491
491
492 $ sendhttpraw << EOF
492 $ sendhttpraw << EOF
493 > httprequest POST api/$HTTPV2/ro/multirequest
493 > httprequest POST api/$HTTPV2/ro/multirequest
494 > accept: $MEDIATYPE
494 > accept: $MEDIATYPE
495 > content-type: $MEDIATYPE
495 > content-type: $MEDIATYPE
496 > user-agent: test
496 > user-agent: test
497 > frame 1 1 stream-begin command-request new|more \xa2Dargs\xa1Inamespace
497 > frame 1 1 stream-begin command-request new|more \xa2Dargs\xa1Inamespace
498 > frame 3 1 0 command-request new|more \xa2Dargs\xa1Inamespace
498 > frame 3 1 0 command-request new|more \xa2Dargs\xa1Inamespace
499 > frame 3 1 0 command-request continuation JnamespacesDnameHlistkeys
499 > frame 3 1 0 command-request continuation JnamespacesDnameHlistkeys
500 > frame 1 1 0 command-request continuation IbookmarksDnameHlistkeys
500 > frame 1 1 0 command-request continuation IbookmarksDnameHlistkeys
501 > EOF
501 > EOF
502 using raw connection to peer
502 using raw connection to peer
503 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
503 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
504 s> Accept-Encoding: identity\r\n
504 s> Accept-Encoding: identity\r\n
505 s> accept: application/mercurial-exp-framing-0005\r\n
505 s> accept: application/mercurial-exp-framing-0005\r\n
506 s> content-type: application/mercurial-exp-framing-0005\r\n
506 s> content-type: application/mercurial-exp-framing-0005\r\n
507 s> user-agent: test\r\n
507 s> user-agent: test\r\n
508 s> content-length: 115\r\n
508 s> content-length: 115\r\n
509 s> host: $LOCALIP:$HGPORT\r\n (glob)
509 s> host: $LOCALIP:$HGPORT\r\n (glob)
510 s> \r\n
510 s> \r\n
511 s> \x11\x00\x00\x01\x00\x01\x01\x15\xa2Dargs\xa1Inamespace\x11\x00\x00\x03\x00\x01\x00\x15\xa2Dargs\xa1Inamespace\x19\x00\x00\x03\x00\x01\x00\x12JnamespacesDnameHlistkeys\x18\x00\x00\x01\x00\x01\x00\x12IbookmarksDnameHlistkeys
511 s> \x11\x00\x00\x01\x00\x01\x01\x15\xa2Dargs\xa1Inamespace\x11\x00\x00\x03\x00\x01\x00\x15\xa2Dargs\xa1Inamespace\x19\x00\x00\x03\x00\x01\x00\x12JnamespacesDnameHlistkeys\x18\x00\x00\x01\x00\x01\x00\x12IbookmarksDnameHlistkeys
512 s> makefile('rb', None)
512 s> makefile('rb', None)
513 s> HTTP/1.1 200 OK\r\n
513 s> HTTP/1.1 200 OK\r\n
514 s> Server: testing stub value\r\n
514 s> Server: testing stub value\r\n
515 s> Date: $HTTP_DATE$\r\n
515 s> Date: $HTTP_DATE$\r\n
516 s> Content-Type: application/mercurial-exp-framing-0005\r\n
516 s> Content-Type: application/mercurial-exp-framing-0005\r\n
517 s> Transfer-Encoding: chunked\r\n
517 s> Transfer-Encoding: chunked\r\n
518 s> \r\n
518 s> \r\n
519 s> 33\r\n
519 s> 33\r\n
520 s> +\x00\x00\x03\x00\x02\x012\xa1FstatusBok\xa3Fphases@Ibookmarks@Jnamespaces@
520 s> +\x00\x00\x03\x00\x02\x012\xa1FstatusBok\xa3Ibookmarks@Jnamespaces@Fphases@
521 s> \r\n
521 s> \r\n
522 s> 14\r\n
522 s> 14\r\n
523 s> \x0c\x00\x00\x01\x00\x02\x002\xa1FstatusBok\xa0
523 s> \x0c\x00\x00\x01\x00\x02\x002\xa1FstatusBok\xa0
524 s> \r\n
524 s> \r\n
525 s> 0\r\n
525 s> 0\r\n
526 s> \r\n
526 s> \r\n
527
527
528 Restart server to disable read-write access
528 Restart server to disable read-write access
529
529
530 $ killdaemons.py
530 $ killdaemons.py
531 $ cat > server/.hg/hgrc << EOF
531 $ cat > server/.hg/hgrc << EOF
532 > [experimental]
532 > [experimental]
533 > web.apiserver = true
533 > web.apiserver = true
534 > web.api.debugreflect = true
534 > web.api.debugreflect = true
535 > web.api.http-v2 = true
535 > web.api.http-v2 = true
536 > [web]
536 > [web]
537 > push_ssl = false
537 > push_ssl = false
538 > EOF
538 > EOF
539
539
540 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
540 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
541 $ cat hg.pid > $DAEMON_PIDS
541 $ cat hg.pid > $DAEMON_PIDS
542
542
543 Attempting to run a read-write command via multirequest on read-only URL is not allowed
543 Attempting to run a read-write command via multirequest on read-only URL is not allowed
544
544
545 $ sendhttpraw << EOF
545 $ sendhttpraw << EOF
546 > httprequest POST api/$HTTPV2/ro/multirequest
546 > httprequest POST api/$HTTPV2/ro/multirequest
547 > accept: $MEDIATYPE
547 > accept: $MEDIATYPE
548 > content-type: $MEDIATYPE
548 > content-type: $MEDIATYPE
549 > user-agent: test
549 > user-agent: test
550 > frame 1 1 stream-begin command-request new cbor:{b'name': b'pushkey'}
550 > frame 1 1 stream-begin command-request new cbor:{b'name': b'pushkey'}
551 > EOF
551 > EOF
552 using raw connection to peer
552 using raw connection to peer
553 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
553 s> POST /api/exp-http-v2-0001/ro/multirequest HTTP/1.1\r\n
554 s> Accept-Encoding: identity\r\n
554 s> Accept-Encoding: identity\r\n
555 s> accept: application/mercurial-exp-framing-0005\r\n
555 s> accept: application/mercurial-exp-framing-0005\r\n
556 s> content-type: application/mercurial-exp-framing-0005\r\n
556 s> content-type: application/mercurial-exp-framing-0005\r\n
557 s> user-agent: test\r\n
557 s> user-agent: test\r\n
558 s> content-length: 22\r\n
558 s> content-length: 22\r\n
559 s> host: $LOCALIP:$HGPORT\r\n (glob)
559 s> host: $LOCALIP:$HGPORT\r\n (glob)
560 s> \r\n
560 s> \r\n
561 s> \x0e\x00\x00\x01\x00\x01\x01\x11\xa1DnameGpushkey
561 s> \x0e\x00\x00\x01\x00\x01\x01\x11\xa1DnameGpushkey
562 s> makefile('rb', None)
562 s> makefile('rb', None)
563 s> HTTP/1.1 403 Forbidden\r\n
563 s> HTTP/1.1 403 Forbidden\r\n
564 s> Server: testing stub value\r\n
564 s> Server: testing stub value\r\n
565 s> Date: $HTTP_DATE$\r\n
565 s> Date: $HTTP_DATE$\r\n
566 s> Content-Type: text/plain\r\n
566 s> Content-Type: text/plain\r\n
567 s> Content-Length: 52\r\n
567 s> Content-Length: 52\r\n
568 s> \r\n
568 s> \r\n
569 s> insufficient permissions to execute command: pushkey
569 s> insufficient permissions to execute command: pushkey
570
570
571 $ cat error.log
571 $ cat error.log
@@ -1,422 +1,422 b''
1 #require no-chg
1 #require no-chg
2
2
3 $ . $TESTDIR/wireprotohelpers.sh
3 $ . $TESTDIR/wireprotohelpers.sh
4
4
5 $ hg init server
5 $ hg init server
6
6
7 zstd isn't present in plain builds. Make tests easier by removing
7 zstd isn't present in plain builds. Make tests easier by removing
8 zstd from the equation.
8 zstd from the equation.
9
9
10 $ cat >> server/.hg/hgrc << EOF
10 $ cat >> server/.hg/hgrc << EOF
11 > [server]
11 > [server]
12 > compressionengines = zlib
12 > compressionengines = zlib
13 > EOF
13 > EOF
14
14
15 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
15 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
16 $ cat hg.pid > $DAEMON_PIDS
16 $ cat hg.pid > $DAEMON_PIDS
17
17
18 A normal capabilities request is serviced for version 1
18 A normal capabilities request is serviced for version 1
19
19
20 $ sendhttpraw << EOF
20 $ sendhttpraw << EOF
21 > httprequest GET ?cmd=capabilities
21 > httprequest GET ?cmd=capabilities
22 > user-agent: test
22 > user-agent: test
23 > EOF
23 > EOF
24 using raw connection to peer
24 using raw connection to peer
25 s> GET /?cmd=capabilities HTTP/1.1\r\n
25 s> GET /?cmd=capabilities HTTP/1.1\r\n
26 s> Accept-Encoding: identity\r\n
26 s> Accept-Encoding: identity\r\n
27 s> user-agent: test\r\n
27 s> user-agent: test\r\n
28 s> host: $LOCALIP:$HGPORT\r\n (glob)
28 s> host: $LOCALIP:$HGPORT\r\n (glob)
29 s> \r\n
29 s> \r\n
30 s> makefile('rb', None)
30 s> makefile('rb', None)
31 s> HTTP/1.1 200 Script output follows\r\n
31 s> HTTP/1.1 200 Script output follows\r\n
32 s> Server: testing stub value\r\n
32 s> Server: testing stub value\r\n
33 s> Date: $HTTP_DATE$\r\n
33 s> Date: $HTTP_DATE$\r\n
34 s> Content-Type: application/mercurial-0.1\r\n
34 s> Content-Type: application/mercurial-0.1\r\n
35 s> Content-Length: *\r\n (glob)
35 s> Content-Length: *\r\n (glob)
36 s> \r\n
36 s> \r\n
37 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
37 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
38
38
39 A proper request without the API server enabled returns the legacy response
39 A proper request without the API server enabled returns the legacy response
40
40
41 $ sendhttpraw << EOF
41 $ sendhttpraw << EOF
42 > httprequest GET ?cmd=capabilities
42 > httprequest GET ?cmd=capabilities
43 > user-agent: test
43 > user-agent: test
44 > x-hgupgrade-1: foo
44 > x-hgupgrade-1: foo
45 > x-hgproto-1: cbor
45 > x-hgproto-1: cbor
46 > EOF
46 > EOF
47 using raw connection to peer
47 using raw connection to peer
48 s> GET /?cmd=capabilities HTTP/1.1\r\n
48 s> GET /?cmd=capabilities HTTP/1.1\r\n
49 s> Accept-Encoding: identity\r\n
49 s> Accept-Encoding: identity\r\n
50 s> user-agent: test\r\n
50 s> user-agent: test\r\n
51 s> x-hgproto-1: cbor\r\n
51 s> x-hgproto-1: cbor\r\n
52 s> x-hgupgrade-1: foo\r\n
52 s> x-hgupgrade-1: foo\r\n
53 s> host: $LOCALIP:$HGPORT\r\n (glob)
53 s> host: $LOCALIP:$HGPORT\r\n (glob)
54 s> \r\n
54 s> \r\n
55 s> makefile('rb', None)
55 s> makefile('rb', None)
56 s> HTTP/1.1 200 Script output follows\r\n
56 s> HTTP/1.1 200 Script output follows\r\n
57 s> Server: testing stub value\r\n
57 s> Server: testing stub value\r\n
58 s> Date: $HTTP_DATE$\r\n
58 s> Date: $HTTP_DATE$\r\n
59 s> Content-Type: application/mercurial-0.1\r\n
59 s> Content-Type: application/mercurial-0.1\r\n
60 s> Content-Length: *\r\n (glob)
60 s> Content-Length: *\r\n (glob)
61 s> \r\n
61 s> \r\n
62 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
62 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
63
63
64 Restart with just API server enabled. This enables serving the new format.
64 Restart with just API server enabled. This enables serving the new format.
65
65
66 $ killdaemons.py
66 $ killdaemons.py
67 $ cat error.log
67 $ cat error.log
68
68
69 $ cat >> server/.hg/hgrc << EOF
69 $ cat >> server/.hg/hgrc << EOF
70 > [experimental]
70 > [experimental]
71 > web.apiserver = true
71 > web.apiserver = true
72 > EOF
72 > EOF
73
73
74 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
74 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
75 $ cat hg.pid > $DAEMON_PIDS
75 $ cat hg.pid > $DAEMON_PIDS
76
76
77 X-HgUpgrade-<N> without CBOR advertisement uses legacy response
77 X-HgUpgrade-<N> without CBOR advertisement uses legacy response
78
78
79 $ sendhttpraw << EOF
79 $ sendhttpraw << EOF
80 > httprequest GET ?cmd=capabilities
80 > httprequest GET ?cmd=capabilities
81 > user-agent: test
81 > user-agent: test
82 > x-hgupgrade-1: foo bar
82 > x-hgupgrade-1: foo bar
83 > EOF
83 > EOF
84 using raw connection to peer
84 using raw connection to peer
85 s> GET /?cmd=capabilities HTTP/1.1\r\n
85 s> GET /?cmd=capabilities HTTP/1.1\r\n
86 s> Accept-Encoding: identity\r\n
86 s> Accept-Encoding: identity\r\n
87 s> user-agent: test\r\n
87 s> user-agent: test\r\n
88 s> x-hgupgrade-1: foo bar\r\n
88 s> x-hgupgrade-1: foo bar\r\n
89 s> host: $LOCALIP:$HGPORT\r\n (glob)
89 s> host: $LOCALIP:$HGPORT\r\n (glob)
90 s> \r\n
90 s> \r\n
91 s> makefile('rb', None)
91 s> makefile('rb', None)
92 s> HTTP/1.1 200 Script output follows\r\n
92 s> HTTP/1.1 200 Script output follows\r\n
93 s> Server: testing stub value\r\n
93 s> Server: testing stub value\r\n
94 s> Date: $HTTP_DATE$\r\n
94 s> Date: $HTTP_DATE$\r\n
95 s> Content-Type: application/mercurial-0.1\r\n
95 s> Content-Type: application/mercurial-0.1\r\n
96 s> Content-Length: *\r\n (glob)
96 s> Content-Length: *\r\n (glob)
97 s> \r\n
97 s> \r\n
98 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
98 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
99
99
100 X-HgUpgrade-<N> without known serialization in X-HgProto-<N> uses legacy response
100 X-HgUpgrade-<N> without known serialization in X-HgProto-<N> uses legacy response
101
101
102 $ sendhttpraw << EOF
102 $ sendhttpraw << EOF
103 > httprequest GET ?cmd=capabilities
103 > httprequest GET ?cmd=capabilities
104 > user-agent: test
104 > user-agent: test
105 > x-hgupgrade-1: foo bar
105 > x-hgupgrade-1: foo bar
106 > x-hgproto-1: some value
106 > x-hgproto-1: some value
107 > EOF
107 > EOF
108 using raw connection to peer
108 using raw connection to peer
109 s> GET /?cmd=capabilities HTTP/1.1\r\n
109 s> GET /?cmd=capabilities HTTP/1.1\r\n
110 s> Accept-Encoding: identity\r\n
110 s> Accept-Encoding: identity\r\n
111 s> user-agent: test\r\n
111 s> user-agent: test\r\n
112 s> x-hgproto-1: some value\r\n
112 s> x-hgproto-1: some value\r\n
113 s> x-hgupgrade-1: foo bar\r\n
113 s> x-hgupgrade-1: foo bar\r\n
114 s> host: $LOCALIP:$HGPORT\r\n (glob)
114 s> host: $LOCALIP:$HGPORT\r\n (glob)
115 s> \r\n
115 s> \r\n
116 s> makefile('rb', None)
116 s> makefile('rb', None)
117 s> HTTP/1.1 200 Script output follows\r\n
117 s> HTTP/1.1 200 Script output follows\r\n
118 s> Server: testing stub value\r\n
118 s> Server: testing stub value\r\n
119 s> Date: $HTTP_DATE$\r\n
119 s> Date: $HTTP_DATE$\r\n
120 s> Content-Type: application/mercurial-0.1\r\n
120 s> Content-Type: application/mercurial-0.1\r\n
121 s> Content-Length: *\r\n (glob)
121 s> Content-Length: *\r\n (glob)
122 s> \r\n
122 s> \r\n
123 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
123 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
124
124
125 X-HgUpgrade-<N> + X-HgProto-<N> headers trigger new response format
125 X-HgUpgrade-<N> + X-HgProto-<N> headers trigger new response format
126
126
127 $ sendhttpraw << EOF
127 $ sendhttpraw << EOF
128 > httprequest GET ?cmd=capabilities
128 > httprequest GET ?cmd=capabilities
129 > user-agent: test
129 > user-agent: test
130 > x-hgupgrade-1: foo bar
130 > x-hgupgrade-1: foo bar
131 > x-hgproto-1: cbor
131 > x-hgproto-1: cbor
132 > EOF
132 > EOF
133 using raw connection to peer
133 using raw connection to peer
134 s> GET /?cmd=capabilities HTTP/1.1\r\n
134 s> GET /?cmd=capabilities HTTP/1.1\r\n
135 s> Accept-Encoding: identity\r\n
135 s> Accept-Encoding: identity\r\n
136 s> user-agent: test\r\n
136 s> user-agent: test\r\n
137 s> x-hgproto-1: cbor\r\n
137 s> x-hgproto-1: cbor\r\n
138 s> x-hgupgrade-1: foo bar\r\n
138 s> x-hgupgrade-1: foo bar\r\n
139 s> host: $LOCALIP:$HGPORT\r\n (glob)
139 s> host: $LOCALIP:$HGPORT\r\n (glob)
140 s> \r\n
140 s> \r\n
141 s> makefile('rb', None)
141 s> makefile('rb', None)
142 s> HTTP/1.1 200 OK\r\n
142 s> HTTP/1.1 200 OK\r\n
143 s> Server: testing stub value\r\n
143 s> Server: testing stub value\r\n
144 s> Date: $HTTP_DATE$\r\n
144 s> Date: $HTTP_DATE$\r\n
145 s> Content-Type: application/mercurial-cbor\r\n
145 s> Content-Type: application/mercurial-cbor\r\n
146 s> Content-Length: *\r\n (glob)
146 s> Content-Length: *\r\n (glob)
147 s> \r\n
147 s> \r\n
148 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
148 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
149 cbor> {
149 cbor> {
150 b'apibase': b'api/',
150 b'apibase': b'api/',
151 b'apis': {},
151 b'apis': {},
152 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
152 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
153 }
153 }
154
154
155 Restart server to enable HTTPv2
155 Restart server to enable HTTPv2
156
156
157 $ killdaemons.py
157 $ killdaemons.py
158 $ enablehttpv2 server
158 $ enablehttpv2 server
159 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
159 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
160 $ cat hg.pid > $DAEMON_PIDS
160 $ cat hg.pid > $DAEMON_PIDS
161
161
162 Only requested API services are returned
162 Only requested API services are returned
163
163
164 $ sendhttpraw << EOF
164 $ sendhttpraw << EOF
165 > httprequest GET ?cmd=capabilities
165 > httprequest GET ?cmd=capabilities
166 > user-agent: test
166 > user-agent: test
167 > x-hgupgrade-1: foo bar
167 > x-hgupgrade-1: foo bar
168 > x-hgproto-1: cbor
168 > x-hgproto-1: cbor
169 > EOF
169 > EOF
170 using raw connection to peer
170 using raw connection to peer
171 s> GET /?cmd=capabilities HTTP/1.1\r\n
171 s> GET /?cmd=capabilities HTTP/1.1\r\n
172 s> Accept-Encoding: identity\r\n
172 s> Accept-Encoding: identity\r\n
173 s> user-agent: test\r\n
173 s> user-agent: test\r\n
174 s> x-hgproto-1: cbor\r\n
174 s> x-hgproto-1: cbor\r\n
175 s> x-hgupgrade-1: foo bar\r\n
175 s> x-hgupgrade-1: foo bar\r\n
176 s> host: $LOCALIP:$HGPORT\r\n (glob)
176 s> host: $LOCALIP:$HGPORT\r\n (glob)
177 s> \r\n
177 s> \r\n
178 s> makefile('rb', None)
178 s> makefile('rb', None)
179 s> HTTP/1.1 200 OK\r\n
179 s> HTTP/1.1 200 OK\r\n
180 s> Server: testing stub value\r\n
180 s> Server: testing stub value\r\n
181 s> Date: $HTTP_DATE$\r\n
181 s> Date: $HTTP_DATE$\r\n
182 s> Content-Type: application/mercurial-cbor\r\n
182 s> Content-Type: application/mercurial-cbor\r\n
183 s> Content-Length: *\r\n (glob)
183 s> Content-Length: *\r\n (glob)
184 s> \r\n
184 s> \r\n
185 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
185 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
186 cbor> {
186 cbor> {
187 b'apibase': b'api/',
187 b'apibase': b'api/',
188 b'apis': {},
188 b'apis': {},
189 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
189 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
190 }
190 }
191
191
192 Request for HTTPv2 service returns information about it
192 Request for HTTPv2 service returns information about it
193
193
194 $ sendhttpraw << EOF
194 $ sendhttpraw << EOF
195 > httprequest GET ?cmd=capabilities
195 > httprequest GET ?cmd=capabilities
196 > user-agent: test
196 > user-agent: test
197 > x-hgupgrade-1: exp-http-v2-0001 foo bar
197 > x-hgupgrade-1: exp-http-v2-0001 foo bar
198 > x-hgproto-1: cbor
198 > x-hgproto-1: cbor
199 > EOF
199 > EOF
200 using raw connection to peer
200 using raw connection to peer
201 s> GET /?cmd=capabilities HTTP/1.1\r\n
201 s> GET /?cmd=capabilities HTTP/1.1\r\n
202 s> Accept-Encoding: identity\r\n
202 s> Accept-Encoding: identity\r\n
203 s> user-agent: test\r\n
203 s> user-agent: test\r\n
204 s> x-hgproto-1: cbor\r\n
204 s> x-hgproto-1: cbor\r\n
205 s> x-hgupgrade-1: exp-http-v2-0001 foo bar\r\n
205 s> x-hgupgrade-1: exp-http-v2-0001 foo bar\r\n
206 s> host: $LOCALIP:$HGPORT\r\n (glob)
206 s> host: $LOCALIP:$HGPORT\r\n (glob)
207 s> \r\n
207 s> \r\n
208 s> makefile('rb', None)
208 s> makefile('rb', None)
209 s> HTTP/1.1 200 OK\r\n
209 s> HTTP/1.1 200 OK\r\n
210 s> Server: testing stub value\r\n
210 s> Server: testing stub value\r\n
211 s> Date: $HTTP_DATE$\r\n
211 s> Date: $HTTP_DATE$\r\n
212 s> Content-Type: application/mercurial-cbor\r\n
212 s> Content-Type: application/mercurial-cbor\r\n
213 s> Content-Length: *\r\n (glob)
213 s> Content-Length: *\r\n (glob)
214 s> \r\n
214 s> \r\n
215 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
215 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
216 cbor> {
216 cbor> {
217 b'apibase': b'api/',
217 b'apibase': b'api/',
218 b'apis': {
218 b'apis': {
219 b'exp-http-v2-0001': {
219 b'exp-http-v2-0001': {
220 b'commands': {
220 b'commands': {
221 b'branchmap': {
221 b'branchmap': {
222 b'args': {},
222 b'args': {},
223 b'permissions': [
223 b'permissions': [
224 b'pull'
224 b'pull'
225 ]
225 ]
226 },
226 },
227 b'capabilities': {
227 b'capabilities': {
228 b'args': {},
228 b'args': {},
229 b'permissions': [
229 b'permissions': [
230 b'pull'
230 b'pull'
231 ]
231 ]
232 },
232 },
233 b'heads': {
233 b'heads': {
234 b'args': {
234 b'args': {
235 b'publiconly': False
235 b'publiconly': False
236 },
236 },
237 b'permissions': [
237 b'permissions': [
238 b'pull'
238 b'pull'
239 ]
239 ]
240 },
240 },
241 b'known': {
241 b'known': {
242 b'args': {
242 b'args': {
243 b'nodes': [
243 b'nodes': [
244 b'deadbeef'
244 b'deadbeef'
245 ]
245 ]
246 },
246 },
247 b'permissions': [
247 b'permissions': [
248 b'pull'
248 b'pull'
249 ]
249 ]
250 },
250 },
251 b'listkeys': {
251 b'listkeys': {
252 b'args': {
252 b'args': {
253 b'namespace': b'ns'
253 b'namespace': b'ns'
254 },
254 },
255 b'permissions': [
255 b'permissions': [
256 b'pull'
256 b'pull'
257 ]
257 ]
258 },
258 },
259 b'lookup': {
259 b'lookup': {
260 b'args': {
260 b'args': {
261 b'key': b'foo'
261 b'key': b'foo'
262 },
262 },
263 b'permissions': [
263 b'permissions': [
264 b'pull'
264 b'pull'
265 ]
265 ]
266 },
266 },
267 b'pushkey': {
267 b'pushkey': {
268 b'args': {
268 b'args': {
269 b'key': b'key',
269 b'key': b'key',
270 b'namespace': b'ns',
270 b'namespace': b'ns',
271 b'new': b'new',
271 b'new': b'new',
272 b'old': b'old'
272 b'old': b'old'
273 },
273 },
274 b'permissions': [
274 b'permissions': [
275 b'push'
275 b'push'
276 ]
276 ]
277 }
277 }
278 },
278 },
279 b'compression': [
279 b'compression': [
280 {
280 {
281 b'name': b'zlib'
281 b'name': b'zlib'
282 }
282 }
283 ],
283 ],
284 b'framingmediatypes': [
284 b'framingmediatypes': [
285 b'application/mercurial-exp-framing-0005'
285 b'application/mercurial-exp-framing-0005'
286 ],
286 ],
287 b'rawrepoformats': [
287 b'rawrepoformats': [
288 b'generaldelta',
288 b'generaldelta',
289 b'revlogv1'
289 b'revlogv1'
290 ]
290 ]
291 }
291 }
292 },
292 },
293 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
293 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
294 }
294 }
295
295
296 capabilities command returns expected info
296 capabilities command returns expected info
297
297
298 $ sendhttpv2peerhandshake << EOF
298 $ sendhttpv2peerhandshake << EOF
299 > command capabilities
299 > command capabilities
300 > EOF
300 > EOF
301 creating http peer for wire protocol version 2
301 creating http peer for wire protocol version 2
302 s> GET /?cmd=capabilities HTTP/1.1\r\n
302 s> GET /?cmd=capabilities HTTP/1.1\r\n
303 s> Accept-Encoding: identity\r\n
303 s> Accept-Encoding: identity\r\n
304 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
304 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
305 s> x-hgproto-1: cbor\r\n
305 s> x-hgproto-1: cbor\r\n
306 s> x-hgupgrade-1: exp-http-v2-0001\r\n
306 s> x-hgupgrade-1: exp-http-v2-0001\r\n
307 s> accept: application/mercurial-0.1\r\n
307 s> accept: application/mercurial-0.1\r\n
308 s> host: $LOCALIP:$HGPORT\r\n (glob)
308 s> host: $LOCALIP:$HGPORT\r\n (glob)
309 s> user-agent: Mercurial debugwireproto\r\n
309 s> user-agent: Mercurial debugwireproto\r\n
310 s> \r\n
310 s> \r\n
311 s> makefile('rb', None)
311 s> makefile('rb', None)
312 s> HTTP/1.1 200 OK\r\n
312 s> HTTP/1.1 200 OK\r\n
313 s> Server: testing stub value\r\n
313 s> Server: testing stub value\r\n
314 s> Date: $HTTP_DATE$\r\n
314 s> Date: $HTTP_DATE$\r\n
315 s> Content-Type: application/mercurial-cbor\r\n
315 s> Content-Type: application/mercurial-cbor\r\n
316 s> Content-Length: *\r\n (glob)
316 s> Content-Length: *\r\n (glob)
317 s> \r\n
317 s> \r\n
318 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
318 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0001\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xc5batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
319 sending capabilities command
319 sending capabilities command
320 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
320 s> POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
321 s> Accept-Encoding: identity\r\n
321 s> Accept-Encoding: identity\r\n
322 s> *\r\n (glob)
322 s> *\r\n (glob)
323 s> content-type: application/mercurial-exp-framing-0005\r\n
323 s> content-type: application/mercurial-exp-framing-0005\r\n
324 s> content-length: 27\r\n
324 s> content-length: 27\r\n
325 s> host: $LOCALIP:$HGPORT\r\n (glob)
325 s> host: $LOCALIP:$HGPORT\r\n (glob)
326 s> user-agent: Mercurial debugwireproto\r\n
326 s> user-agent: Mercurial debugwireproto\r\n
327 s> \r\n
327 s> \r\n
328 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
328 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
329 s> makefile('rb', None)
329 s> makefile('rb', None)
330 s> HTTP/1.1 200 OK\r\n
330 s> HTTP/1.1 200 OK\r\n
331 s> Server: testing stub value\r\n
331 s> Server: testing stub value\r\n
332 s> Date: $HTTP_DATE$\r\n
332 s> Date: $HTTP_DATE$\r\n
333 s> Content-Type: application/mercurial-exp-framing-0005\r\n
333 s> Content-Type: application/mercurial-exp-framing-0005\r\n
334 s> Transfer-Encoding: chunked\r\n
334 s> Transfer-Encoding: chunked\r\n
335 s> \r\n
335 s> \r\n
336 s> 1d7\r\n
336 s> 1d7\r\n
337 s> \xcf\x01\x00\x01\x00\x02\x012
337 s> \xcf\x01\x00\x01\x00\x02\x012
338 s> \xa1FstatusBok\xa4Hcommands\xa7Eheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyCnewCnewColdColdInamespaceBnsKpermissions\x81DpushHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullKcompression\x81\xa1DnameDzlibNrawrepoformats\x82LgeneraldeltaHrevlogv1Qframingmediatypes\x81X&application/mercurial-exp-framing-0005
338 s> \xa1FstatusBok\xa4Hcommands\xa7Ibranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xf4Kpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\x81HdeadbeefKpermissions\x81DpullHlistkeys\xa2Dargs\xa1InamespaceBnsKpermissions\x81DpullFlookup\xa2Dargs\xa1CkeyCfooKpermissions\x81DpullGpushkey\xa2Dargs\xa4CkeyCkeyInamespaceBnsCnewCnewColdColdKpermissions\x81DpushKcompression\x81\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Nrawrepoformats\x82LgeneraldeltaHrevlogv1
339 s> \r\n
339 s> \r\n
340 received frame(size=463; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
340 received frame(size=463; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
341 s> 0\r\n
341 s> 0\r\n
342 s> \r\n
342 s> \r\n
343 response: [
343 response: [
344 {
344 {
345 b'status': b'ok'
345 b'status': b'ok'
346 },
346 },
347 {
347 {
348 b'commands': {
348 b'commands': {
349 b'branchmap': {
349 b'branchmap': {
350 b'args': {},
350 b'args': {},
351 b'permissions': [
351 b'permissions': [
352 b'pull'
352 b'pull'
353 ]
353 ]
354 },
354 },
355 b'capabilities': {
355 b'capabilities': {
356 b'args': {},
356 b'args': {},
357 b'permissions': [
357 b'permissions': [
358 b'pull'
358 b'pull'
359 ]
359 ]
360 },
360 },
361 b'heads': {
361 b'heads': {
362 b'args': {
362 b'args': {
363 b'publiconly': False
363 b'publiconly': False
364 },
364 },
365 b'permissions': [
365 b'permissions': [
366 b'pull'
366 b'pull'
367 ]
367 ]
368 },
368 },
369 b'known': {
369 b'known': {
370 b'args': {
370 b'args': {
371 b'nodes': [
371 b'nodes': [
372 b'deadbeef'
372 b'deadbeef'
373 ]
373 ]
374 },
374 },
375 b'permissions': [
375 b'permissions': [
376 b'pull'
376 b'pull'
377 ]
377 ]
378 },
378 },
379 b'listkeys': {
379 b'listkeys': {
380 b'args': {
380 b'args': {
381 b'namespace': b'ns'
381 b'namespace': b'ns'
382 },
382 },
383 b'permissions': [
383 b'permissions': [
384 b'pull'
384 b'pull'
385 ]
385 ]
386 },
386 },
387 b'lookup': {
387 b'lookup': {
388 b'args': {
388 b'args': {
389 b'key': b'foo'
389 b'key': b'foo'
390 },
390 },
391 b'permissions': [
391 b'permissions': [
392 b'pull'
392 b'pull'
393 ]
393 ]
394 },
394 },
395 b'pushkey': {
395 b'pushkey': {
396 b'args': {
396 b'args': {
397 b'key': b'key',
397 b'key': b'key',
398 b'namespace': b'ns',
398 b'namespace': b'ns',
399 b'new': b'new',
399 b'new': b'new',
400 b'old': b'old'
400 b'old': b'old'
401 },
401 },
402 b'permissions': [
402 b'permissions': [
403 b'push'
403 b'push'
404 ]
404 ]
405 }
405 }
406 },
406 },
407 b'compression': [
407 b'compression': [
408 {
408 {
409 b'name': b'zlib'
409 b'name': b'zlib'
410 }
410 }
411 ],
411 ],
412 b'framingmediatypes': [
412 b'framingmediatypes': [
413 b'application/mercurial-exp-framing-0005'
413 b'application/mercurial-exp-framing-0005'
414 ],
414 ],
415 b'rawrepoformats': [
415 b'rawrepoformats': [
416 b'generaldelta',
416 b'generaldelta',
417 b'revlogv1'
417 b'revlogv1'
418 ]
418 ]
419 }
419 }
420 ]
420 ]
421
421
422 $ cat error.log
422 $ cat error.log
@@ -1,134 +1,134 b''
1 $ . $TESTDIR/wireprotohelpers.sh
1 $ . $TESTDIR/wireprotohelpers.sh
2
2
3 $ hg init server
3 $ hg init server
4 $ enablehttpv2 server
4 $ enablehttpv2 server
5 $ cd server
5 $ cd server
6 $ hg debugdrawdag << EOF
6 $ hg debugdrawdag << EOF
7 > C D
7 > C D
8 > |/
8 > |/
9 > B
9 > B
10 > |
10 > |
11 > A
11 > A
12 > EOF
12 > EOF
13
13
14 $ hg phase --public -r C
14 $ hg phase --public -r C
15 $ hg book -r C @
15 $ hg book -r C @
16
16
17 $ hg log -T '{rev}:{node} {desc}\n'
17 $ hg log -T '{rev}:{node} {desc}\n'
18 3:be0ef73c17ade3fc89dc41701eb9fc3a91b58282 D
18 3:be0ef73c17ade3fc89dc41701eb9fc3a91b58282 D
19 2:26805aba1e600a82e93661149f2313866a221a7b C
19 2:26805aba1e600a82e93661149f2313866a221a7b C
20 1:112478962961147124edd43549aedd1a335e44bf B
20 1:112478962961147124edd43549aedd1a335e44bf B
21 0:426bada5c67598ca65036d57d9e4b64b0c1ce7a0 A
21 0:426bada5c67598ca65036d57d9e4b64b0c1ce7a0 A
22
22
23 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
23 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
24 $ cat hg.pid > $DAEMON_PIDS
24 $ cat hg.pid > $DAEMON_PIDS
25
25
26 Request for namespaces works
26 Request for namespaces works
27
27
28 $ sendhttpv2peer << EOF
28 $ sendhttpv2peer << EOF
29 > command listkeys
29 > command listkeys
30 > namespace namespaces
30 > namespace namespaces
31 > EOF
31 > EOF
32 creating http peer for wire protocol version 2
32 creating http peer for wire protocol version 2
33 sending listkeys command
33 sending listkeys command
34 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
34 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
35 s> Accept-Encoding: identity\r\n
35 s> Accept-Encoding: identity\r\n
36 s> accept: application/mercurial-exp-framing-0005\r\n
36 s> accept: application/mercurial-exp-framing-0005\r\n
37 s> content-type: application/mercurial-exp-framing-0005\r\n
37 s> content-type: application/mercurial-exp-framing-0005\r\n
38 s> content-length: 50\r\n
38 s> content-length: 50\r\n
39 s> host: $LOCALIP:$HGPORT\r\n (glob)
39 s> host: $LOCALIP:$HGPORT\r\n (glob)
40 s> user-agent: Mercurial debugwireproto\r\n
40 s> user-agent: Mercurial debugwireproto\r\n
41 s> \r\n
41 s> \r\n
42 s> *\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceJnamespacesDnameHlistkeys
42 s> *\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceJnamespacesDnameHlistkeys
43 s> makefile('rb', None)
43 s> makefile('rb', None)
44 s> HTTP/1.1 200 OK\r\n
44 s> HTTP/1.1 200 OK\r\n
45 s> Server: testing stub value\r\n
45 s> Server: testing stub value\r\n
46 s> Date: $HTTP_DATE$\r\n
46 s> Date: $HTTP_DATE$\r\n
47 s> Content-Type: application/mercurial-exp-framing-0005\r\n
47 s> Content-Type: application/mercurial-exp-framing-0005\r\n
48 s> Transfer-Encoding: chunked\r\n
48 s> Transfer-Encoding: chunked\r\n
49 s> \r\n
49 s> \r\n
50 s> 33\r\n
50 s> 33\r\n
51 s> +\x00\x00\x01\x00\x02\x012
51 s> +\x00\x00\x01\x00\x02\x012
52 s> \xa1FstatusBok\xa3Fphases@Ibookmarks@Jnamespaces@
52 s> \xa1FstatusBok\xa3Ibookmarks@Jnamespaces@Fphases@
53 s> \r\n
53 s> \r\n
54 received frame(size=43; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
54 received frame(size=43; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
55 s> 0\r\n
55 s> 0\r\n
56 s> \r\n
56 s> \r\n
57 response: {
57 response: {
58 b'bookmarks': b'',
58 b'bookmarks': b'',
59 b'namespaces': b'',
59 b'namespaces': b'',
60 b'phases': b''
60 b'phases': b''
61 }
61 }
62
62
63 Request for phases works
63 Request for phases works
64
64
65 $ sendhttpv2peer << EOF
65 $ sendhttpv2peer << EOF
66 > command listkeys
66 > command listkeys
67 > namespace phases
67 > namespace phases
68 > EOF
68 > EOF
69 creating http peer for wire protocol version 2
69 creating http peer for wire protocol version 2
70 sending listkeys command
70 sending listkeys command
71 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
71 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
72 s> Accept-Encoding: identity\r\n
72 s> Accept-Encoding: identity\r\n
73 s> accept: application/mercurial-exp-framing-0005\r\n
73 s> accept: application/mercurial-exp-framing-0005\r\n
74 s> content-type: application/mercurial-exp-framing-0005\r\n
74 s> content-type: application/mercurial-exp-framing-0005\r\n
75 s> content-length: 46\r\n
75 s> content-length: 46\r\n
76 s> host: $LOCALIP:$HGPORT\r\n (glob)
76 s> host: $LOCALIP:$HGPORT\r\n (glob)
77 s> user-agent: Mercurial debugwireproto\r\n
77 s> user-agent: Mercurial debugwireproto\r\n
78 s> \r\n
78 s> \r\n
79 s> &\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceFphasesDnameHlistkeys
79 s> &\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceFphasesDnameHlistkeys
80 s> makefile('rb', None)
80 s> makefile('rb', None)
81 s> HTTP/1.1 200 OK\r\n
81 s> HTTP/1.1 200 OK\r\n
82 s> Server: testing stub value\r\n
82 s> Server: testing stub value\r\n
83 s> Date: $HTTP_DATE$\r\n
83 s> Date: $HTTP_DATE$\r\n
84 s> Content-Type: application/mercurial-exp-framing-0005\r\n
84 s> Content-Type: application/mercurial-exp-framing-0005\r\n
85 s> Transfer-Encoding: chunked\r\n
85 s> Transfer-Encoding: chunked\r\n
86 s> \r\n
86 s> \r\n
87 s> 50\r\n
87 s> 50\r\n
88 s> H\x00\x00\x01\x00\x02\x012
88 s> H\x00\x00\x01\x00\x02\x012
89 s> \xa1FstatusBok\xa2JpublishingDTrueX(be0ef73c17ade3fc89dc41701eb9fc3a91b58282A1
89 s> \xa1FstatusBok\xa2X(be0ef73c17ade3fc89dc41701eb9fc3a91b58282A1JpublishingDTrue
90 s> \r\n
90 s> \r\n
91 received frame(size=72; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
91 received frame(size=72; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
92 s> 0\r\n
92 s> 0\r\n
93 s> \r\n
93 s> \r\n
94 response: {
94 response: {
95 b'be0ef73c17ade3fc89dc41701eb9fc3a91b58282': b'1',
95 b'be0ef73c17ade3fc89dc41701eb9fc3a91b58282': b'1',
96 b'publishing': b'True'
96 b'publishing': b'True'
97 }
97 }
98
98
99 Request for bookmarks works
99 Request for bookmarks works
100
100
101 $ sendhttpv2peer << EOF
101 $ sendhttpv2peer << EOF
102 > command listkeys
102 > command listkeys
103 > namespace bookmarks
103 > namespace bookmarks
104 > EOF
104 > EOF
105 creating http peer for wire protocol version 2
105 creating http peer for wire protocol version 2
106 sending listkeys command
106 sending listkeys command
107 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
107 s> POST /api/exp-http-v2-0001/ro/listkeys HTTP/1.1\r\n
108 s> Accept-Encoding: identity\r\n
108 s> Accept-Encoding: identity\r\n
109 s> accept: application/mercurial-exp-framing-0005\r\n
109 s> accept: application/mercurial-exp-framing-0005\r\n
110 s> content-type: application/mercurial-exp-framing-0005\r\n
110 s> content-type: application/mercurial-exp-framing-0005\r\n
111 s> content-length: 49\r\n
111 s> content-length: 49\r\n
112 s> host: $LOCALIP:$HGPORT\r\n (glob)
112 s> host: $LOCALIP:$HGPORT\r\n (glob)
113 s> user-agent: Mercurial debugwireproto\r\n
113 s> user-agent: Mercurial debugwireproto\r\n
114 s> \r\n
114 s> \r\n
115 s> )\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceIbookmarksDnameHlistkeys
115 s> )\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa1InamespaceIbookmarksDnameHlistkeys
116 s> makefile('rb', None)
116 s> makefile('rb', None)
117 s> HTTP/1.1 200 OK\r\n
117 s> HTTP/1.1 200 OK\r\n
118 s> Server: testing stub value\r\n
118 s> Server: testing stub value\r\n
119 s> Date: $HTTP_DATE$\r\n
119 s> Date: $HTTP_DATE$\r\n
120 s> Content-Type: application/mercurial-exp-framing-0005\r\n
120 s> Content-Type: application/mercurial-exp-framing-0005\r\n
121 s> Transfer-Encoding: chunked\r\n
121 s> Transfer-Encoding: chunked\r\n
122 s> \r\n
122 s> \r\n
123 s> 40\r\n
123 s> 40\r\n
124 s> 8\x00\x00\x01\x00\x02\x012
124 s> 8\x00\x00\x01\x00\x02\x012
125 s> \xa1FstatusBok\xa1A@X(26805aba1e600a82e93661149f2313866a221a7b
125 s> \xa1FstatusBok\xa1A@X(26805aba1e600a82e93661149f2313866a221a7b
126 s> \r\n
126 s> \r\n
127 received frame(size=56; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
127 received frame(size=56; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
128 s> 0\r\n
128 s> 0\r\n
129 s> \r\n
129 s> \r\n
130 response: {
130 response: {
131 b'@': b'26805aba1e600a82e93661149f2313866a221a7b'
131 b'@': b'26805aba1e600a82e93661149f2313866a221a7b'
132 }
132 }
133
133
134 $ cat error.log
134 $ cat error.log
General Comments 0
You need to be logged in to leave comments. Login now