##// END OF EJS Templates
wireprotoserver: use our CBOR encoder...
Gregory Szorc -
r39478:5f4a9ada default
parent child Browse files
Show More
@@ -1,804 +1,802 b''
1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 #
3 #
4 # This software may be used and distributed according to the terms of the
4 # This software may be used and distributed according to the terms of the
5 # GNU General Public License version 2 or any later version.
5 # GNU General Public License version 2 or any later version.
6
6
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import contextlib
9 import contextlib
10 import struct
10 import struct
11 import sys
11 import sys
12 import threading
12 import threading
13
13
14 from .i18n import _
14 from .i18n import _
15 from .thirdparty import (
16 cbor,
17 )
18 from . import (
15 from . import (
19 encoding,
16 encoding,
20 error,
17 error,
21 pycompat,
18 pycompat,
22 util,
19 util,
23 wireprototypes,
20 wireprototypes,
24 wireprotov1server,
21 wireprotov1server,
25 wireprotov2server,
22 wireprotov2server,
26 )
23 )
27 from .utils import (
24 from .utils import (
25 cborutil,
28 interfaceutil,
26 interfaceutil,
29 procutil,
27 procutil,
30 )
28 )
31
29
32 stringio = util.stringio
30 stringio = util.stringio
33
31
34 urlerr = util.urlerr
32 urlerr = util.urlerr
35 urlreq = util.urlreq
33 urlreq = util.urlreq
36
34
37 HTTP_OK = 200
35 HTTP_OK = 200
38
36
39 HGTYPE = 'application/mercurial-0.1'
37 HGTYPE = 'application/mercurial-0.1'
40 HGTYPE2 = 'application/mercurial-0.2'
38 HGTYPE2 = 'application/mercurial-0.2'
41 HGERRTYPE = 'application/hg-error'
39 HGERRTYPE = 'application/hg-error'
42
40
43 SSHV1 = wireprototypes.SSHV1
41 SSHV1 = wireprototypes.SSHV1
44 SSHV2 = wireprototypes.SSHV2
42 SSHV2 = wireprototypes.SSHV2
45
43
46 def decodevaluefromheaders(req, headerprefix):
44 def decodevaluefromheaders(req, headerprefix):
47 """Decode a long value from multiple HTTP request headers.
45 """Decode a long value from multiple HTTP request headers.
48
46
49 Returns the value as a bytes, not a str.
47 Returns the value as a bytes, not a str.
50 """
48 """
51 chunks = []
49 chunks = []
52 i = 1
50 i = 1
53 while True:
51 while True:
54 v = req.headers.get(b'%s-%d' % (headerprefix, i))
52 v = req.headers.get(b'%s-%d' % (headerprefix, i))
55 if v is None:
53 if v is None:
56 break
54 break
57 chunks.append(pycompat.bytesurl(v))
55 chunks.append(pycompat.bytesurl(v))
58 i += 1
56 i += 1
59
57
60 return ''.join(chunks)
58 return ''.join(chunks)
61
59
62 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
60 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
63 class httpv1protocolhandler(object):
61 class httpv1protocolhandler(object):
64 def __init__(self, req, ui, checkperm):
62 def __init__(self, req, ui, checkperm):
65 self._req = req
63 self._req = req
66 self._ui = ui
64 self._ui = ui
67 self._checkperm = checkperm
65 self._checkperm = checkperm
68 self._protocaps = None
66 self._protocaps = None
69
67
70 @property
68 @property
71 def name(self):
69 def name(self):
72 return 'http-v1'
70 return 'http-v1'
73
71
74 def getargs(self, args):
72 def getargs(self, args):
75 knownargs = self._args()
73 knownargs = self._args()
76 data = {}
74 data = {}
77 keys = args.split()
75 keys = args.split()
78 for k in keys:
76 for k in keys:
79 if k == '*':
77 if k == '*':
80 star = {}
78 star = {}
81 for key in knownargs.keys():
79 for key in knownargs.keys():
82 if key != 'cmd' and key not in keys:
80 if key != 'cmd' and key not in keys:
83 star[key] = knownargs[key][0]
81 star[key] = knownargs[key][0]
84 data['*'] = star
82 data['*'] = star
85 else:
83 else:
86 data[k] = knownargs[k][0]
84 data[k] = knownargs[k][0]
87 return [data[k] for k in keys]
85 return [data[k] for k in keys]
88
86
89 def _args(self):
87 def _args(self):
90 args = self._req.qsparams.asdictoflists()
88 args = self._req.qsparams.asdictoflists()
91 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
89 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
92 if postlen:
90 if postlen:
93 args.update(urlreq.parseqs(
91 args.update(urlreq.parseqs(
94 self._req.bodyfh.read(postlen), keep_blank_values=True))
92 self._req.bodyfh.read(postlen), keep_blank_values=True))
95 return args
93 return args
96
94
97 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
95 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
98 args.update(urlreq.parseqs(argvalue, keep_blank_values=True))
96 args.update(urlreq.parseqs(argvalue, keep_blank_values=True))
99 return args
97 return args
100
98
101 def getprotocaps(self):
99 def getprotocaps(self):
102 if self._protocaps is None:
100 if self._protocaps is None:
103 value = decodevaluefromheaders(self._req, b'X-HgProto')
101 value = decodevaluefromheaders(self._req, b'X-HgProto')
104 self._protocaps = set(value.split(' '))
102 self._protocaps = set(value.split(' '))
105 return self._protocaps
103 return self._protocaps
106
104
107 def getpayload(self):
105 def getpayload(self):
108 # Existing clients *always* send Content-Length.
106 # Existing clients *always* send Content-Length.
109 length = int(self._req.headers[b'Content-Length'])
107 length = int(self._req.headers[b'Content-Length'])
110
108
111 # If httppostargs is used, we need to read Content-Length
109 # If httppostargs is used, we need to read Content-Length
112 # minus the amount that was consumed by args.
110 # minus the amount that was consumed by args.
113 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
111 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
114 return util.filechunkiter(self._req.bodyfh, limit=length)
112 return util.filechunkiter(self._req.bodyfh, limit=length)
115
113
116 @contextlib.contextmanager
114 @contextlib.contextmanager
117 def mayberedirectstdio(self):
115 def mayberedirectstdio(self):
118 oldout = self._ui.fout
116 oldout = self._ui.fout
119 olderr = self._ui.ferr
117 olderr = self._ui.ferr
120
118
121 out = util.stringio()
119 out = util.stringio()
122
120
123 try:
121 try:
124 self._ui.fout = out
122 self._ui.fout = out
125 self._ui.ferr = out
123 self._ui.ferr = out
126 yield out
124 yield out
127 finally:
125 finally:
128 self._ui.fout = oldout
126 self._ui.fout = oldout
129 self._ui.ferr = olderr
127 self._ui.ferr = olderr
130
128
131 def client(self):
129 def client(self):
132 return 'remote:%s:%s:%s' % (
130 return 'remote:%s:%s:%s' % (
133 self._req.urlscheme,
131 self._req.urlscheme,
134 urlreq.quote(self._req.remotehost or ''),
132 urlreq.quote(self._req.remotehost or ''),
135 urlreq.quote(self._req.remoteuser or ''))
133 urlreq.quote(self._req.remoteuser or ''))
136
134
137 def addcapabilities(self, repo, caps):
135 def addcapabilities(self, repo, caps):
138 caps.append(b'batch')
136 caps.append(b'batch')
139
137
140 caps.append('httpheader=%d' %
138 caps.append('httpheader=%d' %
141 repo.ui.configint('server', 'maxhttpheaderlen'))
139 repo.ui.configint('server', 'maxhttpheaderlen'))
142 if repo.ui.configbool('experimental', 'httppostargs'):
140 if repo.ui.configbool('experimental', 'httppostargs'):
143 caps.append('httppostargs')
141 caps.append('httppostargs')
144
142
145 # FUTURE advertise 0.2rx once support is implemented
143 # FUTURE advertise 0.2rx once support is implemented
146 # FUTURE advertise minrx and mintx after consulting config option
144 # FUTURE advertise minrx and mintx after consulting config option
147 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
145 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
148
146
149 compengines = wireprototypes.supportedcompengines(repo.ui,
147 compengines = wireprototypes.supportedcompengines(repo.ui,
150 util.SERVERROLE)
148 util.SERVERROLE)
151 if compengines:
149 if compengines:
152 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
150 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
153 for e in compengines)
151 for e in compengines)
154 caps.append('compression=%s' % comptypes)
152 caps.append('compression=%s' % comptypes)
155
153
156 return caps
154 return caps
157
155
158 def checkperm(self, perm):
156 def checkperm(self, perm):
159 return self._checkperm(perm)
157 return self._checkperm(perm)
160
158
161 # This method exists mostly so that extensions like remotefilelog can
159 # This method exists mostly so that extensions like remotefilelog can
162 # disable a kludgey legacy method only over http. As of early 2018,
160 # disable a kludgey legacy method only over http. As of early 2018,
163 # there are no other known users, so with any luck we can discard this
161 # there are no other known users, so with any luck we can discard this
164 # hook if remotefilelog becomes a first-party extension.
162 # hook if remotefilelog becomes a first-party extension.
165 def iscmd(cmd):
163 def iscmd(cmd):
166 return cmd in wireprotov1server.commands
164 return cmd in wireprotov1server.commands
167
165
168 def handlewsgirequest(rctx, req, res, checkperm):
166 def handlewsgirequest(rctx, req, res, checkperm):
169 """Possibly process a wire protocol request.
167 """Possibly process a wire protocol request.
170
168
171 If the current request is a wire protocol request, the request is
169 If the current request is a wire protocol request, the request is
172 processed by this function.
170 processed by this function.
173
171
174 ``req`` is a ``parsedrequest`` instance.
172 ``req`` is a ``parsedrequest`` instance.
175 ``res`` is a ``wsgiresponse`` instance.
173 ``res`` is a ``wsgiresponse`` instance.
176
174
177 Returns a bool indicating if the request was serviced. If set, the caller
175 Returns a bool indicating if the request was serviced. If set, the caller
178 should stop processing the request, as a response has already been issued.
176 should stop processing the request, as a response has already been issued.
179 """
177 """
180 # Avoid cycle involving hg module.
178 # Avoid cycle involving hg module.
181 from .hgweb import common as hgwebcommon
179 from .hgweb import common as hgwebcommon
182
180
183 repo = rctx.repo
181 repo = rctx.repo
184
182
185 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
183 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
186 # string parameter. If it isn't present, this isn't a wire protocol
184 # string parameter. If it isn't present, this isn't a wire protocol
187 # request.
185 # request.
188 if 'cmd' not in req.qsparams:
186 if 'cmd' not in req.qsparams:
189 return False
187 return False
190
188
191 cmd = req.qsparams['cmd']
189 cmd = req.qsparams['cmd']
192
190
193 # The "cmd" request parameter is used by both the wire protocol and hgweb.
191 # The "cmd" request parameter is used by both the wire protocol and hgweb.
194 # While not all wire protocol commands are available for all transports,
192 # While not all wire protocol commands are available for all transports,
195 # if we see a "cmd" value that resembles a known wire protocol command, we
193 # if we see a "cmd" value that resembles a known wire protocol command, we
196 # route it to a protocol handler. This is better than routing possible
194 # route it to a protocol handler. This is better than routing possible
197 # wire protocol requests to hgweb because it prevents hgweb from using
195 # wire protocol requests to hgweb because it prevents hgweb from using
198 # known wire protocol commands and it is less confusing for machine
196 # known wire protocol commands and it is less confusing for machine
199 # clients.
197 # clients.
200 if not iscmd(cmd):
198 if not iscmd(cmd):
201 return False
199 return False
202
200
203 # The "cmd" query string argument is only valid on the root path of the
201 # The "cmd" query string argument is only valid on the root path of the
204 # repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
202 # repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
205 # like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
203 # like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
206 # in this case. We send an HTTP 404 for backwards compatibility reasons.
204 # in this case. We send an HTTP 404 for backwards compatibility reasons.
207 if req.dispatchpath:
205 if req.dispatchpath:
208 res.status = hgwebcommon.statusmessage(404)
206 res.status = hgwebcommon.statusmessage(404)
209 res.headers['Content-Type'] = HGTYPE
207 res.headers['Content-Type'] = HGTYPE
210 # TODO This is not a good response to issue for this request. This
208 # TODO This is not a good response to issue for this request. This
211 # is mostly for BC for now.
209 # is mostly for BC for now.
212 res.setbodybytes('0\n%s\n' % b'Not Found')
210 res.setbodybytes('0\n%s\n' % b'Not Found')
213 return True
211 return True
214
212
215 proto = httpv1protocolhandler(req, repo.ui,
213 proto = httpv1protocolhandler(req, repo.ui,
216 lambda perm: checkperm(rctx, req, perm))
214 lambda perm: checkperm(rctx, req, perm))
217
215
218 # The permissions checker should be the only thing that can raise an
216 # The permissions checker should be the only thing that can raise an
219 # ErrorResponse. It is kind of a layer violation to catch an hgweb
217 # ErrorResponse. It is kind of a layer violation to catch an hgweb
220 # exception here. So consider refactoring into a exception type that
218 # exception here. So consider refactoring into a exception type that
221 # is associated with the wire protocol.
219 # is associated with the wire protocol.
222 try:
220 try:
223 _callhttp(repo, req, res, proto, cmd)
221 _callhttp(repo, req, res, proto, cmd)
224 except hgwebcommon.ErrorResponse as e:
222 except hgwebcommon.ErrorResponse as e:
225 for k, v in e.headers:
223 for k, v in e.headers:
226 res.headers[k] = v
224 res.headers[k] = v
227 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
225 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
228 # TODO This response body assumes the failed command was
226 # TODO This response body assumes the failed command was
229 # "unbundle." That assumption is not always valid.
227 # "unbundle." That assumption is not always valid.
230 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
228 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
231
229
232 return True
230 return True
233
231
234 def _availableapis(repo):
232 def _availableapis(repo):
235 apis = set()
233 apis = set()
236
234
237 # Registered APIs are made available via config options of the name of
235 # Registered APIs are made available via config options of the name of
238 # the protocol.
236 # the protocol.
239 for k, v in API_HANDLERS.items():
237 for k, v in API_HANDLERS.items():
240 section, option = v['config']
238 section, option = v['config']
241 if repo.ui.configbool(section, option):
239 if repo.ui.configbool(section, option):
242 apis.add(k)
240 apis.add(k)
243
241
244 return apis
242 return apis
245
243
246 def handlewsgiapirequest(rctx, req, res, checkperm):
244 def handlewsgiapirequest(rctx, req, res, checkperm):
247 """Handle requests to /api/*."""
245 """Handle requests to /api/*."""
248 assert req.dispatchparts[0] == b'api'
246 assert req.dispatchparts[0] == b'api'
249
247
250 repo = rctx.repo
248 repo = rctx.repo
251
249
252 # This whole URL space is experimental for now. But we want to
250 # This whole URL space is experimental for now. But we want to
253 # reserve the URL space. So, 404 all URLs if the feature isn't enabled.
251 # reserve the URL space. So, 404 all URLs if the feature isn't enabled.
254 if not repo.ui.configbool('experimental', 'web.apiserver'):
252 if not repo.ui.configbool('experimental', 'web.apiserver'):
255 res.status = b'404 Not Found'
253 res.status = b'404 Not Found'
256 res.headers[b'Content-Type'] = b'text/plain'
254 res.headers[b'Content-Type'] = b'text/plain'
257 res.setbodybytes(_('Experimental API server endpoint not enabled'))
255 res.setbodybytes(_('Experimental API server endpoint not enabled'))
258 return
256 return
259
257
260 # The URL space is /api/<protocol>/*. The structure of URLs under varies
258 # The URL space is /api/<protocol>/*. The structure of URLs under varies
261 # by <protocol>.
259 # by <protocol>.
262
260
263 availableapis = _availableapis(repo)
261 availableapis = _availableapis(repo)
264
262
265 # Requests to /api/ list available APIs.
263 # Requests to /api/ list available APIs.
266 if req.dispatchparts == [b'api']:
264 if req.dispatchparts == [b'api']:
267 res.status = b'200 OK'
265 res.status = b'200 OK'
268 res.headers[b'Content-Type'] = b'text/plain'
266 res.headers[b'Content-Type'] = b'text/plain'
269 lines = [_('APIs can be accessed at /api/<name>, where <name> can be '
267 lines = [_('APIs can be accessed at /api/<name>, where <name> can be '
270 'one of the following:\n')]
268 'one of the following:\n')]
271 if availableapis:
269 if availableapis:
272 lines.extend(sorted(availableapis))
270 lines.extend(sorted(availableapis))
273 else:
271 else:
274 lines.append(_('(no available APIs)\n'))
272 lines.append(_('(no available APIs)\n'))
275 res.setbodybytes(b'\n'.join(lines))
273 res.setbodybytes(b'\n'.join(lines))
276 return
274 return
277
275
278 proto = req.dispatchparts[1]
276 proto = req.dispatchparts[1]
279
277
280 if proto not in API_HANDLERS:
278 if proto not in API_HANDLERS:
281 res.status = b'404 Not Found'
279 res.status = b'404 Not Found'
282 res.headers[b'Content-Type'] = b'text/plain'
280 res.headers[b'Content-Type'] = b'text/plain'
283 res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % (
281 res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % (
284 proto, b', '.join(sorted(availableapis))))
282 proto, b', '.join(sorted(availableapis))))
285 return
283 return
286
284
287 if proto not in availableapis:
285 if proto not in availableapis:
288 res.status = b'404 Not Found'
286 res.status = b'404 Not Found'
289 res.headers[b'Content-Type'] = b'text/plain'
287 res.headers[b'Content-Type'] = b'text/plain'
290 res.setbodybytes(_('API %s not enabled\n') % proto)
288 res.setbodybytes(_('API %s not enabled\n') % proto)
291 return
289 return
292
290
293 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm,
291 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm,
294 req.dispatchparts[2:])
292 req.dispatchparts[2:])
295
293
296 # Maps API name to metadata so custom API can be registered.
294 # Maps API name to metadata so custom API can be registered.
297 # Keys are:
295 # Keys are:
298 #
296 #
299 # config
297 # config
300 # Config option that controls whether service is enabled.
298 # Config option that controls whether service is enabled.
301 # handler
299 # handler
302 # Callable receiving (rctx, req, res, checkperm, urlparts) that is called
300 # Callable receiving (rctx, req, res, checkperm, urlparts) that is called
303 # when a request to this API is received.
301 # when a request to this API is received.
304 # apidescriptor
302 # apidescriptor
305 # Callable receiving (req, repo) that is called to obtain an API
303 # Callable receiving (req, repo) that is called to obtain an API
306 # descriptor for this service. The response must be serializable to CBOR.
304 # descriptor for this service. The response must be serializable to CBOR.
307 API_HANDLERS = {
305 API_HANDLERS = {
308 wireprotov2server.HTTP_WIREPROTO_V2: {
306 wireprotov2server.HTTP_WIREPROTO_V2: {
309 'config': ('experimental', 'web.api.http-v2'),
307 'config': ('experimental', 'web.api.http-v2'),
310 'handler': wireprotov2server.handlehttpv2request,
308 'handler': wireprotov2server.handlehttpv2request,
311 'apidescriptor': wireprotov2server.httpv2apidescriptor,
309 'apidescriptor': wireprotov2server.httpv2apidescriptor,
312 },
310 },
313 }
311 }
314
312
315 def _httpresponsetype(ui, proto, prefer_uncompressed):
313 def _httpresponsetype(ui, proto, prefer_uncompressed):
316 """Determine the appropriate response type and compression settings.
314 """Determine the appropriate response type and compression settings.
317
315
318 Returns a tuple of (mediatype, compengine, engineopts).
316 Returns a tuple of (mediatype, compengine, engineopts).
319 """
317 """
320 # Determine the response media type and compression engine based
318 # Determine the response media type and compression engine based
321 # on the request parameters.
319 # on the request parameters.
322
320
323 if '0.2' in proto.getprotocaps():
321 if '0.2' in proto.getprotocaps():
324 # All clients are expected to support uncompressed data.
322 # All clients are expected to support uncompressed data.
325 if prefer_uncompressed:
323 if prefer_uncompressed:
326 return HGTYPE2, util._noopengine(), {}
324 return HGTYPE2, util._noopengine(), {}
327
325
328 # Now find an agreed upon compression format.
326 # Now find an agreed upon compression format.
329 compformats = wireprotov1server.clientcompressionsupport(proto)
327 compformats = wireprotov1server.clientcompressionsupport(proto)
330 for engine in wireprototypes.supportedcompengines(ui, util.SERVERROLE):
328 for engine in wireprototypes.supportedcompengines(ui, util.SERVERROLE):
331 if engine.wireprotosupport().name in compformats:
329 if engine.wireprotosupport().name in compformats:
332 opts = {}
330 opts = {}
333 level = ui.configint('server', '%slevel' % engine.name())
331 level = ui.configint('server', '%slevel' % engine.name())
334 if level is not None:
332 if level is not None:
335 opts['level'] = level
333 opts['level'] = level
336
334
337 return HGTYPE2, engine, opts
335 return HGTYPE2, engine, opts
338
336
339 # No mutually supported compression format. Fall back to the
337 # No mutually supported compression format. Fall back to the
340 # legacy protocol.
338 # legacy protocol.
341
339
342 # Don't allow untrusted settings because disabling compression or
340 # Don't allow untrusted settings because disabling compression or
343 # setting a very high compression level could lead to flooding
341 # setting a very high compression level could lead to flooding
344 # the server's network or CPU.
342 # the server's network or CPU.
345 opts = {'level': ui.configint('server', 'zliblevel')}
343 opts = {'level': ui.configint('server', 'zliblevel')}
346 return HGTYPE, util.compengines['zlib'], opts
344 return HGTYPE, util.compengines['zlib'], opts
347
345
348 def processcapabilitieshandshake(repo, req, res, proto):
346 def processcapabilitieshandshake(repo, req, res, proto):
349 """Called during a ?cmd=capabilities request.
347 """Called during a ?cmd=capabilities request.
350
348
351 If the client is advertising support for a newer protocol, we send
349 If the client is advertising support for a newer protocol, we send
352 a CBOR response with information about available services. If no
350 a CBOR response with information about available services. If no
353 advertised services are available, we don't handle the request.
351 advertised services are available, we don't handle the request.
354 """
352 """
355 # Fall back to old behavior unless the API server is enabled.
353 # Fall back to old behavior unless the API server is enabled.
356 if not repo.ui.configbool('experimental', 'web.apiserver'):
354 if not repo.ui.configbool('experimental', 'web.apiserver'):
357 return False
355 return False
358
356
359 clientapis = decodevaluefromheaders(req, b'X-HgUpgrade')
357 clientapis = decodevaluefromheaders(req, b'X-HgUpgrade')
360 protocaps = decodevaluefromheaders(req, b'X-HgProto')
358 protocaps = decodevaluefromheaders(req, b'X-HgProto')
361 if not clientapis or not protocaps:
359 if not clientapis or not protocaps:
362 return False
360 return False
363
361
364 # We currently only support CBOR responses.
362 # We currently only support CBOR responses.
365 protocaps = set(protocaps.split(' '))
363 protocaps = set(protocaps.split(' '))
366 if b'cbor' not in protocaps:
364 if b'cbor' not in protocaps:
367 return False
365 return False
368
366
369 descriptors = {}
367 descriptors = {}
370
368
371 for api in sorted(set(clientapis.split()) & _availableapis(repo)):
369 for api in sorted(set(clientapis.split()) & _availableapis(repo)):
372 handler = API_HANDLERS[api]
370 handler = API_HANDLERS[api]
373
371
374 descriptorfn = handler.get('apidescriptor')
372 descriptorfn = handler.get('apidescriptor')
375 if not descriptorfn:
373 if not descriptorfn:
376 continue
374 continue
377
375
378 descriptors[api] = descriptorfn(req, repo)
376 descriptors[api] = descriptorfn(req, repo)
379
377
380 v1caps = wireprotov1server.dispatch(repo, proto, 'capabilities')
378 v1caps = wireprotov1server.dispatch(repo, proto, 'capabilities')
381 assert isinstance(v1caps, wireprototypes.bytesresponse)
379 assert isinstance(v1caps, wireprototypes.bytesresponse)
382
380
383 m = {
381 m = {
384 # TODO allow this to be configurable.
382 # TODO allow this to be configurable.
385 'apibase': 'api/',
383 'apibase': 'api/',
386 'apis': descriptors,
384 'apis': descriptors,
387 'v1capabilities': v1caps.data,
385 'v1capabilities': v1caps.data,
388 }
386 }
389
387
390 res.status = b'200 OK'
388 res.status = b'200 OK'
391 res.headers[b'Content-Type'] = b'application/mercurial-cbor'
389 res.headers[b'Content-Type'] = b'application/mercurial-cbor'
392 res.setbodybytes(cbor.dumps(m, canonical=True))
390 res.setbodybytes(b''.join(cborutil.streamencode(m)))
393
391
394 return True
392 return True
395
393
396 def _callhttp(repo, req, res, proto, cmd):
394 def _callhttp(repo, req, res, proto, cmd):
397 # Avoid cycle involving hg module.
395 # Avoid cycle involving hg module.
398 from .hgweb import common as hgwebcommon
396 from .hgweb import common as hgwebcommon
399
397
400 def genversion2(gen, engine, engineopts):
398 def genversion2(gen, engine, engineopts):
401 # application/mercurial-0.2 always sends a payload header
399 # application/mercurial-0.2 always sends a payload header
402 # identifying the compression engine.
400 # identifying the compression engine.
403 name = engine.wireprotosupport().name
401 name = engine.wireprotosupport().name
404 assert 0 < len(name) < 256
402 assert 0 < len(name) < 256
405 yield struct.pack('B', len(name))
403 yield struct.pack('B', len(name))
406 yield name
404 yield name
407
405
408 for chunk in gen:
406 for chunk in gen:
409 yield chunk
407 yield chunk
410
408
411 def setresponse(code, contenttype, bodybytes=None, bodygen=None):
409 def setresponse(code, contenttype, bodybytes=None, bodygen=None):
412 if code == HTTP_OK:
410 if code == HTTP_OK:
413 res.status = '200 Script output follows'
411 res.status = '200 Script output follows'
414 else:
412 else:
415 res.status = hgwebcommon.statusmessage(code)
413 res.status = hgwebcommon.statusmessage(code)
416
414
417 res.headers['Content-Type'] = contenttype
415 res.headers['Content-Type'] = contenttype
418
416
419 if bodybytes is not None:
417 if bodybytes is not None:
420 res.setbodybytes(bodybytes)
418 res.setbodybytes(bodybytes)
421 if bodygen is not None:
419 if bodygen is not None:
422 res.setbodygen(bodygen)
420 res.setbodygen(bodygen)
423
421
424 if not wireprotov1server.commands.commandavailable(cmd, proto):
422 if not wireprotov1server.commands.commandavailable(cmd, proto):
425 setresponse(HTTP_OK, HGERRTYPE,
423 setresponse(HTTP_OK, HGERRTYPE,
426 _('requested wire protocol command is not available over '
424 _('requested wire protocol command is not available over '
427 'HTTP'))
425 'HTTP'))
428 return
426 return
429
427
430 proto.checkperm(wireprotov1server.commands[cmd].permission)
428 proto.checkperm(wireprotov1server.commands[cmd].permission)
431
429
432 # Possibly handle a modern client wanting to switch protocols.
430 # Possibly handle a modern client wanting to switch protocols.
433 if (cmd == 'capabilities' and
431 if (cmd == 'capabilities' and
434 processcapabilitieshandshake(repo, req, res, proto)):
432 processcapabilitieshandshake(repo, req, res, proto)):
435
433
436 return
434 return
437
435
438 rsp = wireprotov1server.dispatch(repo, proto, cmd)
436 rsp = wireprotov1server.dispatch(repo, proto, cmd)
439
437
440 if isinstance(rsp, bytes):
438 if isinstance(rsp, bytes):
441 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
439 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
442 elif isinstance(rsp, wireprototypes.bytesresponse):
440 elif isinstance(rsp, wireprototypes.bytesresponse):
443 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp.data)
441 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp.data)
444 elif isinstance(rsp, wireprototypes.streamreslegacy):
442 elif isinstance(rsp, wireprototypes.streamreslegacy):
445 setresponse(HTTP_OK, HGTYPE, bodygen=rsp.gen)
443 setresponse(HTTP_OK, HGTYPE, bodygen=rsp.gen)
446 elif isinstance(rsp, wireprototypes.streamres):
444 elif isinstance(rsp, wireprototypes.streamres):
447 gen = rsp.gen
445 gen = rsp.gen
448
446
449 # This code for compression should not be streamres specific. It
447 # This code for compression should not be streamres specific. It
450 # is here because we only compress streamres at the moment.
448 # is here because we only compress streamres at the moment.
451 mediatype, engine, engineopts = _httpresponsetype(
449 mediatype, engine, engineopts = _httpresponsetype(
452 repo.ui, proto, rsp.prefer_uncompressed)
450 repo.ui, proto, rsp.prefer_uncompressed)
453 gen = engine.compressstream(gen, engineopts)
451 gen = engine.compressstream(gen, engineopts)
454
452
455 if mediatype == HGTYPE2:
453 if mediatype == HGTYPE2:
456 gen = genversion2(gen, engine, engineopts)
454 gen = genversion2(gen, engine, engineopts)
457
455
458 setresponse(HTTP_OK, mediatype, bodygen=gen)
456 setresponse(HTTP_OK, mediatype, bodygen=gen)
459 elif isinstance(rsp, wireprototypes.pushres):
457 elif isinstance(rsp, wireprototypes.pushres):
460 rsp = '%d\n%s' % (rsp.res, rsp.output)
458 rsp = '%d\n%s' % (rsp.res, rsp.output)
461 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
459 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
462 elif isinstance(rsp, wireprototypes.pusherr):
460 elif isinstance(rsp, wireprototypes.pusherr):
463 rsp = '0\n%s\n' % rsp.res
461 rsp = '0\n%s\n' % rsp.res
464 res.drain = True
462 res.drain = True
465 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
463 setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
466 elif isinstance(rsp, wireprototypes.ooberror):
464 elif isinstance(rsp, wireprototypes.ooberror):
467 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
465 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
468 else:
466 else:
469 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
467 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
470
468
471 def _sshv1respondbytes(fout, value):
469 def _sshv1respondbytes(fout, value):
472 """Send a bytes response for protocol version 1."""
470 """Send a bytes response for protocol version 1."""
473 fout.write('%d\n' % len(value))
471 fout.write('%d\n' % len(value))
474 fout.write(value)
472 fout.write(value)
475 fout.flush()
473 fout.flush()
476
474
477 def _sshv1respondstream(fout, source):
475 def _sshv1respondstream(fout, source):
478 write = fout.write
476 write = fout.write
479 for chunk in source.gen:
477 for chunk in source.gen:
480 write(chunk)
478 write(chunk)
481 fout.flush()
479 fout.flush()
482
480
483 def _sshv1respondooberror(fout, ferr, rsp):
481 def _sshv1respondooberror(fout, ferr, rsp):
484 ferr.write(b'%s\n-\n' % rsp)
482 ferr.write(b'%s\n-\n' % rsp)
485 ferr.flush()
483 ferr.flush()
486 fout.write(b'\n')
484 fout.write(b'\n')
487 fout.flush()
485 fout.flush()
488
486
489 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
487 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
490 class sshv1protocolhandler(object):
488 class sshv1protocolhandler(object):
491 """Handler for requests services via version 1 of SSH protocol."""
489 """Handler for requests services via version 1 of SSH protocol."""
492 def __init__(self, ui, fin, fout):
490 def __init__(self, ui, fin, fout):
493 self._ui = ui
491 self._ui = ui
494 self._fin = fin
492 self._fin = fin
495 self._fout = fout
493 self._fout = fout
496 self._protocaps = set()
494 self._protocaps = set()
497
495
498 @property
496 @property
499 def name(self):
497 def name(self):
500 return wireprototypes.SSHV1
498 return wireprototypes.SSHV1
501
499
502 def getargs(self, args):
500 def getargs(self, args):
503 data = {}
501 data = {}
504 keys = args.split()
502 keys = args.split()
505 for n in pycompat.xrange(len(keys)):
503 for n in pycompat.xrange(len(keys)):
506 argline = self._fin.readline()[:-1]
504 argline = self._fin.readline()[:-1]
507 arg, l = argline.split()
505 arg, l = argline.split()
508 if arg not in keys:
506 if arg not in keys:
509 raise error.Abort(_("unexpected parameter %r") % arg)
507 raise error.Abort(_("unexpected parameter %r") % arg)
510 if arg == '*':
508 if arg == '*':
511 star = {}
509 star = {}
512 for k in pycompat.xrange(int(l)):
510 for k in pycompat.xrange(int(l)):
513 argline = self._fin.readline()[:-1]
511 argline = self._fin.readline()[:-1]
514 arg, l = argline.split()
512 arg, l = argline.split()
515 val = self._fin.read(int(l))
513 val = self._fin.read(int(l))
516 star[arg] = val
514 star[arg] = val
517 data['*'] = star
515 data['*'] = star
518 else:
516 else:
519 val = self._fin.read(int(l))
517 val = self._fin.read(int(l))
520 data[arg] = val
518 data[arg] = val
521 return [data[k] for k in keys]
519 return [data[k] for k in keys]
522
520
523 def getprotocaps(self):
521 def getprotocaps(self):
524 return self._protocaps
522 return self._protocaps
525
523
526 def getpayload(self):
524 def getpayload(self):
527 # We initially send an empty response. This tells the client it is
525 # We initially send an empty response. This tells the client it is
528 # OK to start sending data. If a client sees any other response, it
526 # OK to start sending data. If a client sees any other response, it
529 # interprets it as an error.
527 # interprets it as an error.
530 _sshv1respondbytes(self._fout, b'')
528 _sshv1respondbytes(self._fout, b'')
531
529
532 # The file is in the form:
530 # The file is in the form:
533 #
531 #
534 # <chunk size>\n<chunk>
532 # <chunk size>\n<chunk>
535 # ...
533 # ...
536 # 0\n
534 # 0\n
537 count = int(self._fin.readline())
535 count = int(self._fin.readline())
538 while count:
536 while count:
539 yield self._fin.read(count)
537 yield self._fin.read(count)
540 count = int(self._fin.readline())
538 count = int(self._fin.readline())
541
539
542 @contextlib.contextmanager
540 @contextlib.contextmanager
543 def mayberedirectstdio(self):
541 def mayberedirectstdio(self):
544 yield None
542 yield None
545
543
546 def client(self):
544 def client(self):
547 client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
545 client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
548 return 'remote:ssh:' + client
546 return 'remote:ssh:' + client
549
547
550 def addcapabilities(self, repo, caps):
548 def addcapabilities(self, repo, caps):
551 if self.name == wireprototypes.SSHV1:
549 if self.name == wireprototypes.SSHV1:
552 caps.append(b'protocaps')
550 caps.append(b'protocaps')
553 caps.append(b'batch')
551 caps.append(b'batch')
554 return caps
552 return caps
555
553
556 def checkperm(self, perm):
554 def checkperm(self, perm):
557 pass
555 pass
558
556
559 class sshv2protocolhandler(sshv1protocolhandler):
557 class sshv2protocolhandler(sshv1protocolhandler):
560 """Protocol handler for version 2 of the SSH protocol."""
558 """Protocol handler for version 2 of the SSH protocol."""
561
559
562 @property
560 @property
563 def name(self):
561 def name(self):
564 return wireprototypes.SSHV2
562 return wireprototypes.SSHV2
565
563
566 def addcapabilities(self, repo, caps):
564 def addcapabilities(self, repo, caps):
567 return caps
565 return caps
568
566
569 def _runsshserver(ui, repo, fin, fout, ev):
567 def _runsshserver(ui, repo, fin, fout, ev):
570 # This function operates like a state machine of sorts. The following
568 # This function operates like a state machine of sorts. The following
571 # states are defined:
569 # states are defined:
572 #
570 #
573 # protov1-serving
571 # protov1-serving
574 # Server is in protocol version 1 serving mode. Commands arrive on
572 # Server is in protocol version 1 serving mode. Commands arrive on
575 # new lines. These commands are processed in this state, one command
573 # new lines. These commands are processed in this state, one command
576 # after the other.
574 # after the other.
577 #
575 #
578 # protov2-serving
576 # protov2-serving
579 # Server is in protocol version 2 serving mode.
577 # Server is in protocol version 2 serving mode.
580 #
578 #
581 # upgrade-initial
579 # upgrade-initial
582 # The server is going to process an upgrade request.
580 # The server is going to process an upgrade request.
583 #
581 #
584 # upgrade-v2-filter-legacy-handshake
582 # upgrade-v2-filter-legacy-handshake
585 # The protocol is being upgraded to version 2. The server is expecting
583 # The protocol is being upgraded to version 2. The server is expecting
586 # the legacy handshake from version 1.
584 # the legacy handshake from version 1.
587 #
585 #
588 # upgrade-v2-finish
586 # upgrade-v2-finish
589 # The upgrade to version 2 of the protocol is imminent.
587 # The upgrade to version 2 of the protocol is imminent.
590 #
588 #
591 # shutdown
589 # shutdown
592 # The server is shutting down, possibly in reaction to a client event.
590 # The server is shutting down, possibly in reaction to a client event.
593 #
591 #
594 # And here are their transitions:
592 # And here are their transitions:
595 #
593 #
596 # protov1-serving -> shutdown
594 # protov1-serving -> shutdown
597 # When server receives an empty request or encounters another
595 # When server receives an empty request or encounters another
598 # error.
596 # error.
599 #
597 #
600 # protov1-serving -> upgrade-initial
598 # protov1-serving -> upgrade-initial
601 # An upgrade request line was seen.
599 # An upgrade request line was seen.
602 #
600 #
603 # upgrade-initial -> upgrade-v2-filter-legacy-handshake
601 # upgrade-initial -> upgrade-v2-filter-legacy-handshake
604 # Upgrade to version 2 in progress. Server is expecting to
602 # Upgrade to version 2 in progress. Server is expecting to
605 # process a legacy handshake.
603 # process a legacy handshake.
606 #
604 #
607 # upgrade-v2-filter-legacy-handshake -> shutdown
605 # upgrade-v2-filter-legacy-handshake -> shutdown
608 # Client did not fulfill upgrade handshake requirements.
606 # Client did not fulfill upgrade handshake requirements.
609 #
607 #
610 # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
608 # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
611 # Client fulfilled version 2 upgrade requirements. Finishing that
609 # Client fulfilled version 2 upgrade requirements. Finishing that
612 # upgrade.
610 # upgrade.
613 #
611 #
614 # upgrade-v2-finish -> protov2-serving
612 # upgrade-v2-finish -> protov2-serving
615 # Protocol upgrade to version 2 complete. Server can now speak protocol
613 # Protocol upgrade to version 2 complete. Server can now speak protocol
616 # version 2.
614 # version 2.
617 #
615 #
618 # protov2-serving -> protov1-serving
616 # protov2-serving -> protov1-serving
619 # Ths happens by default since protocol version 2 is the same as
617 # Ths happens by default since protocol version 2 is the same as
620 # version 1 except for the handshake.
618 # version 1 except for the handshake.
621
619
622 state = 'protov1-serving'
620 state = 'protov1-serving'
623 proto = sshv1protocolhandler(ui, fin, fout)
621 proto = sshv1protocolhandler(ui, fin, fout)
624 protoswitched = False
622 protoswitched = False
625
623
626 while not ev.is_set():
624 while not ev.is_set():
627 if state == 'protov1-serving':
625 if state == 'protov1-serving':
628 # Commands are issued on new lines.
626 # Commands are issued on new lines.
629 request = fin.readline()[:-1]
627 request = fin.readline()[:-1]
630
628
631 # Empty lines signal to terminate the connection.
629 # Empty lines signal to terminate the connection.
632 if not request:
630 if not request:
633 state = 'shutdown'
631 state = 'shutdown'
634 continue
632 continue
635
633
636 # It looks like a protocol upgrade request. Transition state to
634 # It looks like a protocol upgrade request. Transition state to
637 # handle it.
635 # handle it.
638 if request.startswith(b'upgrade '):
636 if request.startswith(b'upgrade '):
639 if protoswitched:
637 if protoswitched:
640 _sshv1respondooberror(fout, ui.ferr,
638 _sshv1respondooberror(fout, ui.ferr,
641 b'cannot upgrade protocols multiple '
639 b'cannot upgrade protocols multiple '
642 b'times')
640 b'times')
643 state = 'shutdown'
641 state = 'shutdown'
644 continue
642 continue
645
643
646 state = 'upgrade-initial'
644 state = 'upgrade-initial'
647 continue
645 continue
648
646
649 available = wireprotov1server.commands.commandavailable(
647 available = wireprotov1server.commands.commandavailable(
650 request, proto)
648 request, proto)
651
649
652 # This command isn't available. Send an empty response and go
650 # This command isn't available. Send an empty response and go
653 # back to waiting for a new command.
651 # back to waiting for a new command.
654 if not available:
652 if not available:
655 _sshv1respondbytes(fout, b'')
653 _sshv1respondbytes(fout, b'')
656 continue
654 continue
657
655
658 rsp = wireprotov1server.dispatch(repo, proto, request)
656 rsp = wireprotov1server.dispatch(repo, proto, request)
659
657
660 if isinstance(rsp, bytes):
658 if isinstance(rsp, bytes):
661 _sshv1respondbytes(fout, rsp)
659 _sshv1respondbytes(fout, rsp)
662 elif isinstance(rsp, wireprototypes.bytesresponse):
660 elif isinstance(rsp, wireprototypes.bytesresponse):
663 _sshv1respondbytes(fout, rsp.data)
661 _sshv1respondbytes(fout, rsp.data)
664 elif isinstance(rsp, wireprototypes.streamres):
662 elif isinstance(rsp, wireprototypes.streamres):
665 _sshv1respondstream(fout, rsp)
663 _sshv1respondstream(fout, rsp)
666 elif isinstance(rsp, wireprototypes.streamreslegacy):
664 elif isinstance(rsp, wireprototypes.streamreslegacy):
667 _sshv1respondstream(fout, rsp)
665 _sshv1respondstream(fout, rsp)
668 elif isinstance(rsp, wireprototypes.pushres):
666 elif isinstance(rsp, wireprototypes.pushres):
669 _sshv1respondbytes(fout, b'')
667 _sshv1respondbytes(fout, b'')
670 _sshv1respondbytes(fout, b'%d' % rsp.res)
668 _sshv1respondbytes(fout, b'%d' % rsp.res)
671 elif isinstance(rsp, wireprototypes.pusherr):
669 elif isinstance(rsp, wireprototypes.pusherr):
672 _sshv1respondbytes(fout, rsp.res)
670 _sshv1respondbytes(fout, rsp.res)
673 elif isinstance(rsp, wireprototypes.ooberror):
671 elif isinstance(rsp, wireprototypes.ooberror):
674 _sshv1respondooberror(fout, ui.ferr, rsp.message)
672 _sshv1respondooberror(fout, ui.ferr, rsp.message)
675 else:
673 else:
676 raise error.ProgrammingError('unhandled response type from '
674 raise error.ProgrammingError('unhandled response type from '
677 'wire protocol command: %s' % rsp)
675 'wire protocol command: %s' % rsp)
678
676
679 # For now, protocol version 2 serving just goes back to version 1.
677 # For now, protocol version 2 serving just goes back to version 1.
680 elif state == 'protov2-serving':
678 elif state == 'protov2-serving':
681 state = 'protov1-serving'
679 state = 'protov1-serving'
682 continue
680 continue
683
681
684 elif state == 'upgrade-initial':
682 elif state == 'upgrade-initial':
685 # We should never transition into this state if we've switched
683 # We should never transition into this state if we've switched
686 # protocols.
684 # protocols.
687 assert not protoswitched
685 assert not protoswitched
688 assert proto.name == wireprototypes.SSHV1
686 assert proto.name == wireprototypes.SSHV1
689
687
690 # Expected: upgrade <token> <capabilities>
688 # Expected: upgrade <token> <capabilities>
691 # If we get something else, the request is malformed. It could be
689 # If we get something else, the request is malformed. It could be
692 # from a future client that has altered the upgrade line content.
690 # from a future client that has altered the upgrade line content.
693 # We treat this as an unknown command.
691 # We treat this as an unknown command.
694 try:
692 try:
695 token, caps = request.split(b' ')[1:]
693 token, caps = request.split(b' ')[1:]
696 except ValueError:
694 except ValueError:
697 _sshv1respondbytes(fout, b'')
695 _sshv1respondbytes(fout, b'')
698 state = 'protov1-serving'
696 state = 'protov1-serving'
699 continue
697 continue
700
698
701 # Send empty response if we don't support upgrading protocols.
699 # Send empty response if we don't support upgrading protocols.
702 if not ui.configbool('experimental', 'sshserver.support-v2'):
700 if not ui.configbool('experimental', 'sshserver.support-v2'):
703 _sshv1respondbytes(fout, b'')
701 _sshv1respondbytes(fout, b'')
704 state = 'protov1-serving'
702 state = 'protov1-serving'
705 continue
703 continue
706
704
707 try:
705 try:
708 caps = urlreq.parseqs(caps)
706 caps = urlreq.parseqs(caps)
709 except ValueError:
707 except ValueError:
710 _sshv1respondbytes(fout, b'')
708 _sshv1respondbytes(fout, b'')
711 state = 'protov1-serving'
709 state = 'protov1-serving'
712 continue
710 continue
713
711
714 # We don't see an upgrade request to protocol version 2. Ignore
712 # We don't see an upgrade request to protocol version 2. Ignore
715 # the upgrade request.
713 # the upgrade request.
716 wantedprotos = caps.get(b'proto', [b''])[0]
714 wantedprotos = caps.get(b'proto', [b''])[0]
717 if SSHV2 not in wantedprotos:
715 if SSHV2 not in wantedprotos:
718 _sshv1respondbytes(fout, b'')
716 _sshv1respondbytes(fout, b'')
719 state = 'protov1-serving'
717 state = 'protov1-serving'
720 continue
718 continue
721
719
722 # It looks like we can honor this upgrade request to protocol 2.
720 # It looks like we can honor this upgrade request to protocol 2.
723 # Filter the rest of the handshake protocol request lines.
721 # Filter the rest of the handshake protocol request lines.
724 state = 'upgrade-v2-filter-legacy-handshake'
722 state = 'upgrade-v2-filter-legacy-handshake'
725 continue
723 continue
726
724
727 elif state == 'upgrade-v2-filter-legacy-handshake':
725 elif state == 'upgrade-v2-filter-legacy-handshake':
728 # Client should have sent legacy handshake after an ``upgrade``
726 # Client should have sent legacy handshake after an ``upgrade``
729 # request. Expected lines:
727 # request. Expected lines:
730 #
728 #
731 # hello
729 # hello
732 # between
730 # between
733 # pairs 81
731 # pairs 81
734 # 0000...-0000...
732 # 0000...-0000...
735
733
736 ok = True
734 ok = True
737 for line in (b'hello', b'between', b'pairs 81'):
735 for line in (b'hello', b'between', b'pairs 81'):
738 request = fin.readline()[:-1]
736 request = fin.readline()[:-1]
739
737
740 if request != line:
738 if request != line:
741 _sshv1respondooberror(fout, ui.ferr,
739 _sshv1respondooberror(fout, ui.ferr,
742 b'malformed handshake protocol: '
740 b'malformed handshake protocol: '
743 b'missing %s' % line)
741 b'missing %s' % line)
744 ok = False
742 ok = False
745 state = 'shutdown'
743 state = 'shutdown'
746 break
744 break
747
745
748 if not ok:
746 if not ok:
749 continue
747 continue
750
748
751 request = fin.read(81)
749 request = fin.read(81)
752 if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
750 if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
753 _sshv1respondooberror(fout, ui.ferr,
751 _sshv1respondooberror(fout, ui.ferr,
754 b'malformed handshake protocol: '
752 b'malformed handshake protocol: '
755 b'missing between argument value')
753 b'missing between argument value')
756 state = 'shutdown'
754 state = 'shutdown'
757 continue
755 continue
758
756
759 state = 'upgrade-v2-finish'
757 state = 'upgrade-v2-finish'
760 continue
758 continue
761
759
762 elif state == 'upgrade-v2-finish':
760 elif state == 'upgrade-v2-finish':
763 # Send the upgrade response.
761 # Send the upgrade response.
764 fout.write(b'upgraded %s %s\n' % (token, SSHV2))
762 fout.write(b'upgraded %s %s\n' % (token, SSHV2))
765 servercaps = wireprotov1server.capabilities(repo, proto)
763 servercaps = wireprotov1server.capabilities(repo, proto)
766 rsp = b'capabilities: %s' % servercaps.data
764 rsp = b'capabilities: %s' % servercaps.data
767 fout.write(b'%d\n%s\n' % (len(rsp), rsp))
765 fout.write(b'%d\n%s\n' % (len(rsp), rsp))
768 fout.flush()
766 fout.flush()
769
767
770 proto = sshv2protocolhandler(ui, fin, fout)
768 proto = sshv2protocolhandler(ui, fin, fout)
771 protoswitched = True
769 protoswitched = True
772
770
773 state = 'protov2-serving'
771 state = 'protov2-serving'
774 continue
772 continue
775
773
776 elif state == 'shutdown':
774 elif state == 'shutdown':
777 break
775 break
778
776
779 else:
777 else:
780 raise error.ProgrammingError('unhandled ssh server state: %s' %
778 raise error.ProgrammingError('unhandled ssh server state: %s' %
781 state)
779 state)
782
780
783 class sshserver(object):
781 class sshserver(object):
784 def __init__(self, ui, repo, logfh=None):
782 def __init__(self, ui, repo, logfh=None):
785 self._ui = ui
783 self._ui = ui
786 self._repo = repo
784 self._repo = repo
787 self._fin, self._fout = procutil.protectstdio(ui.fin, ui.fout)
785 self._fin, self._fout = procutil.protectstdio(ui.fin, ui.fout)
788
786
789 # Log write I/O to stdout and stderr if configured.
787 # Log write I/O to stdout and stderr if configured.
790 if logfh:
788 if logfh:
791 self._fout = util.makeloggingfileobject(
789 self._fout = util.makeloggingfileobject(
792 logfh, self._fout, 'o', logdata=True)
790 logfh, self._fout, 'o', logdata=True)
793 ui.ferr = util.makeloggingfileobject(
791 ui.ferr = util.makeloggingfileobject(
794 logfh, ui.ferr, 'e', logdata=True)
792 logfh, ui.ferr, 'e', logdata=True)
795
793
796 def serve_forever(self):
794 def serve_forever(self):
797 self.serveuntil(threading.Event())
795 self.serveuntil(threading.Event())
798 procutil.restorestdio(self._ui.fin, self._ui.fout,
796 procutil.restorestdio(self._ui.fin, self._ui.fout,
799 self._fin, self._fout)
797 self._fin, self._fout)
800 sys.exit(0)
798 sys.exit(0)
801
799
802 def serveuntil(self, ev):
800 def serveuntil(self, ev):
803 """Serve until a threading.Event is set."""
801 """Serve until a threading.Event is set."""
804 _runsshserver(self._ui, self._repo, self._fin, self._fout, ev)
802 _runsshserver(self._ui, self._repo, self._fin, self._fout, ev)
@@ -1,740 +1,740 b''
1 #require no-chg
1 #require no-chg
2
2
3 $ . $TESTDIR/wireprotohelpers.sh
3 $ . $TESTDIR/wireprotohelpers.sh
4
4
5 $ cat >> $HGRCPATH << EOF
5 $ cat >> $HGRCPATH << EOF
6 > [web]
6 > [web]
7 > push_ssl = false
7 > push_ssl = false
8 > allow_push = *
8 > allow_push = *
9 > EOF
9 > EOF
10
10
11 $ hg init server
11 $ hg init server
12 $ cd server
12 $ cd server
13 $ touch a
13 $ touch a
14 $ hg -q commit -A -m initial
14 $ hg -q commit -A -m initial
15 $ cd ..
15 $ cd ..
16
16
17 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
17 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
18 $ cat hg.pid >> $DAEMON_PIDS
18 $ cat hg.pid >> $DAEMON_PIDS
19
19
20 compression formats are advertised in compression capability
20 compression formats are advertised in compression capability
21
21
22 #if zstd
22 #if zstd
23 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zstd,zlib$' > /dev/null
23 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zstd,zlib$' > /dev/null
24 #else
24 #else
25 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zlib$' > /dev/null
25 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zlib$' > /dev/null
26 #endif
26 #endif
27
27
28 $ killdaemons.py
28 $ killdaemons.py
29
29
30 server.compressionengines can replace engines list wholesale
30 server.compressionengines can replace engines list wholesale
31
31
32 $ hg serve --config server.compressionengines=none -R server -p $HGPORT -d --pid-file hg.pid
32 $ hg serve --config server.compressionengines=none -R server -p $HGPORT -d --pid-file hg.pid
33 $ cat hg.pid > $DAEMON_PIDS
33 $ cat hg.pid > $DAEMON_PIDS
34 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none$' > /dev/null
34 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none$' > /dev/null
35
35
36 $ killdaemons.py
36 $ killdaemons.py
37
37
38 Order of engines can also change
38 Order of engines can also change
39
39
40 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
40 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
41 $ cat hg.pid > $DAEMON_PIDS
41 $ cat hg.pid > $DAEMON_PIDS
42 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none,zlib$' > /dev/null
42 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none,zlib$' > /dev/null
43
43
44 $ killdaemons.py
44 $ killdaemons.py
45
45
46 Start a default server again
46 Start a default server again
47
47
48 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
48 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
49 $ cat hg.pid > $DAEMON_PIDS
49 $ cat hg.pid > $DAEMON_PIDS
50
50
51 Server should send application/mercurial-0.1 to clients if no Accept is used
51 Server should send application/mercurial-0.1 to clients if no Accept is used
52
52
53 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
53 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
54 200 Script output follows
54 200 Script output follows
55 content-type: application/mercurial-0.1
55 content-type: application/mercurial-0.1
56 date: $HTTP_DATE$
56 date: $HTTP_DATE$
57 server: testing stub value
57 server: testing stub value
58 transfer-encoding: chunked
58 transfer-encoding: chunked
59
59
60 Server should send application/mercurial-0.1 when client says it wants it
60 Server should send application/mercurial-0.1 when client says it wants it
61
61
62 $ get-with-headers.py --hgproto '0.1' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
62 $ get-with-headers.py --hgproto '0.1' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
63 200 Script output follows
63 200 Script output follows
64 content-type: application/mercurial-0.1
64 content-type: application/mercurial-0.1
65 date: $HTTP_DATE$
65 date: $HTTP_DATE$
66 server: testing stub value
66 server: testing stub value
67 transfer-encoding: chunked
67 transfer-encoding: chunked
68
68
69 Server should send application/mercurial-0.2 when client says it wants it
69 Server should send application/mercurial-0.2 when client says it wants it
70
70
71 $ get-with-headers.py --hgproto '0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
71 $ get-with-headers.py --hgproto '0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
72 200 Script output follows
72 200 Script output follows
73 content-type: application/mercurial-0.2
73 content-type: application/mercurial-0.2
74 date: $HTTP_DATE$
74 date: $HTTP_DATE$
75 server: testing stub value
75 server: testing stub value
76 transfer-encoding: chunked
76 transfer-encoding: chunked
77
77
78 $ get-with-headers.py --hgproto '0.1 0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
78 $ get-with-headers.py --hgproto '0.1 0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
79 200 Script output follows
79 200 Script output follows
80 content-type: application/mercurial-0.2
80 content-type: application/mercurial-0.2
81 date: $HTTP_DATE$
81 date: $HTTP_DATE$
82 server: testing stub value
82 server: testing stub value
83 transfer-encoding: chunked
83 transfer-encoding: chunked
84
84
85 Requesting a compression format that server doesn't support results will fall back to 0.1
85 Requesting a compression format that server doesn't support results will fall back to 0.1
86
86
87 $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
87 $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
88 200 Script output follows
88 200 Script output follows
89 content-type: application/mercurial-0.1
89 content-type: application/mercurial-0.1
90 date: $HTTP_DATE$
90 date: $HTTP_DATE$
91 server: testing stub value
91 server: testing stub value
92 transfer-encoding: chunked
92 transfer-encoding: chunked
93
93
94 #if zstd
94 #if zstd
95 zstd is used if available
95 zstd is used if available
96
96
97 $ get-with-headers.py --hgproto '0.2 comp=zstd' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
97 $ get-with-headers.py --hgproto '0.2 comp=zstd' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
98 $ f --size --hexdump --bytes 36 --sha1 resp
98 $ f --size --hexdump --bytes 36 --sha1 resp
99 resp: size=248, sha1=4d8d8f87fb82bd542ce52881fdc94f850748
99 resp: size=248, sha1=4d8d8f87fb82bd542ce52881fdc94f850748
100 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
100 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
101 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 73 74 64 |t follows...zstd|
101 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 73 74 64 |t follows...zstd|
102 0020: 28 b5 2f fd |(./.|
102 0020: 28 b5 2f fd |(./.|
103
103
104 #endif
104 #endif
105
105
106 application/mercurial-0.2 is not yet used on non-streaming responses
106 application/mercurial-0.2 is not yet used on non-streaming responses
107
107
108 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=heads' -
108 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=heads' -
109 200 Script output follows
109 200 Script output follows
110 content-length: 41
110 content-length: 41
111 content-type: application/mercurial-0.1
111 content-type: application/mercurial-0.1
112 date: $HTTP_DATE$
112 date: $HTTP_DATE$
113 server: testing stub value
113 server: testing stub value
114
114
115 e93700bd72895c5addab234c56d4024b487a362f
115 e93700bd72895c5addab234c56d4024b487a362f
116
116
117 Now test protocol preference usage
117 Now test protocol preference usage
118
118
119 $ killdaemons.py
119 $ killdaemons.py
120 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
120 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
121 $ cat hg.pid > $DAEMON_PIDS
121 $ cat hg.pid > $DAEMON_PIDS
122
122
123 No Accept will send 0.1+zlib, even though "none" is preferred b/c "none" isn't supported on 0.1
123 No Accept will send 0.1+zlib, even though "none" is preferred b/c "none" isn't supported on 0.1
124
124
125 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' Content-Type
125 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' Content-Type
126 200 Script output follows
126 200 Script output follows
127 content-type: application/mercurial-0.1
127 content-type: application/mercurial-0.1
128
128
129 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
129 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
130 $ f --size --hexdump --bytes 28 --sha1 resp
130 $ f --size --hexdump --bytes 28 --sha1 resp
131 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
131 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
132 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
132 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
133 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
133 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
134
134
135 Explicit 0.1 will send zlib because "none" isn't supported on 0.1
135 Explicit 0.1 will send zlib because "none" isn't supported on 0.1
136
136
137 $ get-with-headers.py --hgproto '0.1' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
137 $ get-with-headers.py --hgproto '0.1' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
138 $ f --size --hexdump --bytes 28 --sha1 resp
138 $ f --size --hexdump --bytes 28 --sha1 resp
139 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
139 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
140 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
140 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
141 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
141 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
142
142
143 0.2 with no compression will get "none" because that is server's preference
143 0.2 with no compression will get "none" because that is server's preference
144 (spec says ZL and UN are implicitly supported)
144 (spec says ZL and UN are implicitly supported)
145
145
146 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
146 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
147 $ f --size --hexdump --bytes 32 --sha1 resp
147 $ f --size --hexdump --bytes 32 --sha1 resp
148 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
148 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
149 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
149 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
150 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
150 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
151
151
152 Client receives server preference even if local order doesn't match
152 Client receives server preference even if local order doesn't match
153
153
154 $ get-with-headers.py --hgproto '0.2 comp=zlib,none' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
154 $ get-with-headers.py --hgproto '0.2 comp=zlib,none' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
155 $ f --size --hexdump --bytes 32 --sha1 resp
155 $ f --size --hexdump --bytes 32 --sha1 resp
156 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
156 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
157 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
157 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
158 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
158 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
159
159
160 Client receives only supported format even if not server preferred format
160 Client receives only supported format even if not server preferred format
161
161
162 $ get-with-headers.py --hgproto '0.2 comp=zlib' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
162 $ get-with-headers.py --hgproto '0.2 comp=zlib' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
163 $ f --size --hexdump --bytes 33 --sha1 resp
163 $ f --size --hexdump --bytes 33 --sha1 resp
164 resp: size=232, sha1=a1c727f0c9693ca15742a75c30419bc36
164 resp: size=232, sha1=a1c727f0c9693ca15742a75c30419bc36
165 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
165 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
166 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib|
166 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib|
167 0020: 78 |x|
167 0020: 78 |x|
168
168
169 $ killdaemons.py
169 $ killdaemons.py
170 $ cd ..
170 $ cd ..
171
171
172 Test listkeys for listing namespaces
172 Test listkeys for listing namespaces
173
173
174 $ hg init empty
174 $ hg init empty
175 $ hg -R empty serve -p $HGPORT -d --pid-file hg.pid
175 $ hg -R empty serve -p $HGPORT -d --pid-file hg.pid
176 $ cat hg.pid > $DAEMON_PIDS
176 $ cat hg.pid > $DAEMON_PIDS
177
177
178 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
178 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
179 > command listkeys
179 > command listkeys
180 > namespace namespaces
180 > namespace namespaces
181 > EOF
181 > EOF
182 s> GET /?cmd=capabilities HTTP/1.1\r\n
182 s> GET /?cmd=capabilities HTTP/1.1\r\n
183 s> Accept-Encoding: identity\r\n
183 s> Accept-Encoding: identity\r\n
184 s> accept: application/mercurial-0.1\r\n
184 s> accept: application/mercurial-0.1\r\n
185 s> host: $LOCALIP:$HGPORT\r\n (glob)
185 s> host: $LOCALIP:$HGPORT\r\n (glob)
186 s> user-agent: Mercurial debugwireproto\r\n
186 s> user-agent: Mercurial debugwireproto\r\n
187 s> \r\n
187 s> \r\n
188 s> makefile('rb', None)
188 s> makefile('rb', None)
189 s> HTTP/1.1 200 Script output follows\r\n
189 s> HTTP/1.1 200 Script output follows\r\n
190 s> Server: testing stub value\r\n
190 s> Server: testing stub value\r\n
191 s> Date: $HTTP_DATE$\r\n
191 s> Date: $HTTP_DATE$\r\n
192 s> Content-Type: application/mercurial-0.1\r\n
192 s> Content-Type: application/mercurial-0.1\r\n
193 s> Content-Length: *\r\n (glob)
193 s> Content-Length: *\r\n (glob)
194 s> \r\n
194 s> \r\n
195 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
195 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
196 sending listkeys command
196 sending listkeys command
197 s> GET /?cmd=listkeys HTTP/1.1\r\n
197 s> GET /?cmd=listkeys HTTP/1.1\r\n
198 s> Accept-Encoding: identity\r\n
198 s> Accept-Encoding: identity\r\n
199 s> vary: X-HgArg-1,X-HgProto-1\r\n
199 s> vary: X-HgArg-1,X-HgProto-1\r\n
200 s> x-hgarg-1: namespace=namespaces\r\n
200 s> x-hgarg-1: namespace=namespaces\r\n
201 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
201 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
202 s> accept: application/mercurial-0.1\r\n
202 s> accept: application/mercurial-0.1\r\n
203 s> host: $LOCALIP:$HGPORT\r\n (glob)
203 s> host: $LOCALIP:$HGPORT\r\n (glob)
204 s> user-agent: Mercurial debugwireproto\r\n
204 s> user-agent: Mercurial debugwireproto\r\n
205 s> \r\n
205 s> \r\n
206 s> makefile('rb', None)
206 s> makefile('rb', None)
207 s> HTTP/1.1 200 Script output follows\r\n
207 s> HTTP/1.1 200 Script output follows\r\n
208 s> Server: testing stub value\r\n
208 s> Server: testing stub value\r\n
209 s> Date: $HTTP_DATE$\r\n
209 s> Date: $HTTP_DATE$\r\n
210 s> Content-Type: application/mercurial-0.1\r\n
210 s> Content-Type: application/mercurial-0.1\r\n
211 s> Content-Length: 30\r\n
211 s> Content-Length: 30\r\n
212 s> \r\n
212 s> \r\n
213 s> bookmarks\t\n
213 s> bookmarks\t\n
214 s> namespaces\t\n
214 s> namespaces\t\n
215 s> phases\t
215 s> phases\t
216 response: {
216 response: {
217 b'bookmarks': b'',
217 b'bookmarks': b'',
218 b'namespaces': b'',
218 b'namespaces': b'',
219 b'phases': b''
219 b'phases': b''
220 }
220 }
221
221
222 Same thing, but with "httprequest" command
222 Same thing, but with "httprequest" command
223
223
224 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
224 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
225 > httprequest GET ?cmd=listkeys
225 > httprequest GET ?cmd=listkeys
226 > user-agent: test
226 > user-agent: test
227 > x-hgarg-1: namespace=namespaces
227 > x-hgarg-1: namespace=namespaces
228 > EOF
228 > EOF
229 using raw connection to peer
229 using raw connection to peer
230 s> GET /?cmd=listkeys HTTP/1.1\r\n
230 s> GET /?cmd=listkeys HTTP/1.1\r\n
231 s> Accept-Encoding: identity\r\n
231 s> Accept-Encoding: identity\r\n
232 s> user-agent: test\r\n
232 s> user-agent: test\r\n
233 s> x-hgarg-1: namespace=namespaces\r\n
233 s> x-hgarg-1: namespace=namespaces\r\n
234 s> host: $LOCALIP:$HGPORT\r\n (glob)
234 s> host: $LOCALIP:$HGPORT\r\n (glob)
235 s> \r\n
235 s> \r\n
236 s> makefile('rb', None)
236 s> makefile('rb', None)
237 s> HTTP/1.1 200 Script output follows\r\n
237 s> HTTP/1.1 200 Script output follows\r\n
238 s> Server: testing stub value\r\n
238 s> Server: testing stub value\r\n
239 s> Date: $HTTP_DATE$\r\n
239 s> Date: $HTTP_DATE$\r\n
240 s> Content-Type: application/mercurial-0.1\r\n
240 s> Content-Type: application/mercurial-0.1\r\n
241 s> Content-Length: 30\r\n
241 s> Content-Length: 30\r\n
242 s> \r\n
242 s> \r\n
243 s> bookmarks\t\n
243 s> bookmarks\t\n
244 s> namespaces\t\n
244 s> namespaces\t\n
245 s> phases\t
245 s> phases\t
246
246
247 Client with HTTPv2 enabled advertises that and gets old capabilities response from old server
247 Client with HTTPv2 enabled advertises that and gets old capabilities response from old server
248
248
249 $ hg --config experimental.httppeer.advertise-v2=true --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
249 $ hg --config experimental.httppeer.advertise-v2=true --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
250 > command heads
250 > command heads
251 > EOF
251 > EOF
252 s> GET /?cmd=capabilities HTTP/1.1\r\n
252 s> GET /?cmd=capabilities HTTP/1.1\r\n
253 s> Accept-Encoding: identity\r\n
253 s> Accept-Encoding: identity\r\n
254 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
254 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
255 s> x-hgproto-1: cbor\r\n
255 s> x-hgproto-1: cbor\r\n
256 s> x-hgupgrade-1: exp-http-v2-0001\r\n
256 s> x-hgupgrade-1: exp-http-v2-0001\r\n
257 s> accept: application/mercurial-0.1\r\n
257 s> accept: application/mercurial-0.1\r\n
258 s> host: $LOCALIP:$HGPORT\r\n (glob)
258 s> host: $LOCALIP:$HGPORT\r\n (glob)
259 s> user-agent: Mercurial debugwireproto\r\n
259 s> user-agent: Mercurial debugwireproto\r\n
260 s> \r\n
260 s> \r\n
261 s> makefile('rb', None)
261 s> makefile('rb', None)
262 s> HTTP/1.1 200 Script output follows\r\n
262 s> HTTP/1.1 200 Script output follows\r\n
263 s> Server: testing stub value\r\n
263 s> Server: testing stub value\r\n
264 s> Date: $HTTP_DATE$\r\n
264 s> Date: $HTTP_DATE$\r\n
265 s> Content-Type: application/mercurial-0.1\r\n
265 s> Content-Type: application/mercurial-0.1\r\n
266 s> Content-Length: *\r\n (glob)
266 s> Content-Length: *\r\n (glob)
267 s> \r\n
267 s> \r\n
268 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
268 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
269 sending heads command
269 sending heads command
270 s> GET /?cmd=heads HTTP/1.1\r\n
270 s> GET /?cmd=heads HTTP/1.1\r\n
271 s> Accept-Encoding: identity\r\n
271 s> Accept-Encoding: identity\r\n
272 s> vary: X-HgProto-1\r\n
272 s> vary: X-HgProto-1\r\n
273 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
273 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
274 s> accept: application/mercurial-0.1\r\n
274 s> accept: application/mercurial-0.1\r\n
275 s> host: $LOCALIP:$HGPORT\r\n (glob)
275 s> host: $LOCALIP:$HGPORT\r\n (glob)
276 s> user-agent: Mercurial debugwireproto\r\n
276 s> user-agent: Mercurial debugwireproto\r\n
277 s> \r\n
277 s> \r\n
278 s> makefile('rb', None)
278 s> makefile('rb', None)
279 s> HTTP/1.1 200 Script output follows\r\n
279 s> HTTP/1.1 200 Script output follows\r\n
280 s> Server: testing stub value\r\n
280 s> Server: testing stub value\r\n
281 s> Date: $HTTP_DATE$\r\n
281 s> Date: $HTTP_DATE$\r\n
282 s> Content-Type: application/mercurial-0.1\r\n
282 s> Content-Type: application/mercurial-0.1\r\n
283 s> Content-Length: 41\r\n
283 s> Content-Length: 41\r\n
284 s> \r\n
284 s> \r\n
285 s> 0000000000000000000000000000000000000000\n
285 s> 0000000000000000000000000000000000000000\n
286 response: [
286 response: [
287 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
287 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
288 ]
288 ]
289
289
290 $ killdaemons.py
290 $ killdaemons.py
291 $ enablehttpv2 empty
291 $ enablehttpv2 empty
292 $ hg --config server.compressionengines=zlib -R empty serve -p $HGPORT -d --pid-file hg.pid
292 $ hg --config server.compressionengines=zlib -R empty serve -p $HGPORT -d --pid-file hg.pid
293 $ cat hg.pid > $DAEMON_PIDS
293 $ cat hg.pid > $DAEMON_PIDS
294
294
295 Client with HTTPv2 enabled automatically upgrades if the server supports it
295 Client with HTTPv2 enabled automatically upgrades if the server supports it
296
296
297 $ hg --config experimental.httppeer.advertise-v2=true --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
297 $ hg --config experimental.httppeer.advertise-v2=true --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
298 > command heads
298 > command heads
299 > EOF
299 > EOF
300 s> GET /?cmd=capabilities HTTP/1.1\r\n
300 s> GET /?cmd=capabilities HTTP/1.1\r\n
301 s> Accept-Encoding: identity\r\n
301 s> Accept-Encoding: identity\r\n
302 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
302 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
303 s> x-hgproto-1: cbor\r\n
303 s> x-hgproto-1: cbor\r\n
304 s> x-hgupgrade-1: exp-http-v2-0001\r\n
304 s> x-hgupgrade-1: exp-http-v2-0001\r\n
305 s> accept: application/mercurial-0.1\r\n
305 s> accept: application/mercurial-0.1\r\n
306 s> host: $LOCALIP:$HGPORT\r\n (glob)
306 s> host: $LOCALIP:$HGPORT\r\n (glob)
307 s> user-agent: Mercurial debugwireproto\r\n
307 s> user-agent: Mercurial debugwireproto\r\n
308 s> \r\n
308 s> \r\n
309 s> makefile('rb', None)
309 s> makefile('rb', None)
310 s> HTTP/1.1 200 OK\r\n
310 s> HTTP/1.1 200 OK\r\n
311 s> Server: testing stub value\r\n
311 s> Server: testing stub value\r\n
312 s> Date: $HTTP_DATE$\r\n
312 s> Date: $HTTP_DATE$\r\n
313 s> Content-Type: application/mercurial-cbor\r\n
313 s> Content-Type: application/mercurial-cbor\r\n
314 s> Content-Length: *\r\n (glob)
314 s> Content-Length: *\r\n (glob)
315 s> \r\n
315 s> \r\n
316 s> \xa3Dapis\xa1Pexp-http-v2-0001\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-0005GapibaseDapi/Nv1capabilitiesY\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
316 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
317 sending heads command
317 sending heads command
318 s> POST /api/exp-http-v2-0001/ro/heads HTTP/1.1\r\n
318 s> POST /api/exp-http-v2-0001/ro/heads HTTP/1.1\r\n
319 s> Accept-Encoding: identity\r\n
319 s> Accept-Encoding: identity\r\n
320 s> accept: application/mercurial-exp-framing-0005\r\n
320 s> accept: application/mercurial-exp-framing-0005\r\n
321 s> content-type: application/mercurial-exp-framing-0005\r\n
321 s> content-type: application/mercurial-exp-framing-0005\r\n
322 s> content-length: 20\r\n
322 s> content-length: 20\r\n
323 s> host: $LOCALIP:$HGPORT\r\n (glob)
323 s> host: $LOCALIP:$HGPORT\r\n (glob)
324 s> user-agent: Mercurial debugwireproto\r\n
324 s> user-agent: Mercurial debugwireproto\r\n
325 s> \r\n
325 s> \r\n
326 s> \x0c\x00\x00\x01\x00\x01\x01\x11\xa1DnameEheads
326 s> \x0c\x00\x00\x01\x00\x01\x01\x11\xa1DnameEheads
327 s> makefile('rb', None)
327 s> makefile('rb', None)
328 s> HTTP/1.1 200 OK\r\n
328 s> HTTP/1.1 200 OK\r\n
329 s> Server: testing stub value\r\n
329 s> Server: testing stub value\r\n
330 s> Date: $HTTP_DATE$\r\n
330 s> Date: $HTTP_DATE$\r\n
331 s> Content-Type: application/mercurial-exp-framing-0005\r\n
331 s> Content-Type: application/mercurial-exp-framing-0005\r\n
332 s> Transfer-Encoding: chunked\r\n
332 s> Transfer-Encoding: chunked\r\n
333 s> \r\n
333 s> \r\n
334 s> 29\r\n
334 s> 29\r\n
335 s> !\x00\x00\x01\x00\x02\x012
335 s> !\x00\x00\x01\x00\x02\x012
336 s> \xa1FstatusBok\x81T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
336 s> \xa1FstatusBok\x81T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
337 s> \r\n
337 s> \r\n
338 received frame(size=33; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
338 received frame(size=33; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
339 s> 0\r\n
339 s> 0\r\n
340 s> \r\n
340 s> \r\n
341 response: [
341 response: [
342 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
342 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
343 ]
343 ]
344
344
345 $ killdaemons.py
345 $ killdaemons.py
346
346
347 HTTP client follows HTTP redirect on handshake to new repo
347 HTTP client follows HTTP redirect on handshake to new repo
348
348
349 $ cd $TESTTMP
349 $ cd $TESTTMP
350
350
351 $ hg init redirector
351 $ hg init redirector
352 $ hg init redirected
352 $ hg init redirected
353 $ cd redirected
353 $ cd redirected
354 $ touch foo
354 $ touch foo
355 $ hg -q commit -A -m initial
355 $ hg -q commit -A -m initial
356 $ cd ..
356 $ cd ..
357
357
358 $ cat > paths.conf << EOF
358 $ cat > paths.conf << EOF
359 > [paths]
359 > [paths]
360 > / = $TESTTMP/*
360 > / = $TESTTMP/*
361 > EOF
361 > EOF
362
362
363 $ cat > redirectext.py << EOF
363 $ cat > redirectext.py << EOF
364 > from mercurial import extensions, wireprotoserver
364 > from mercurial import extensions, wireprotoserver
365 > def wrappedcallhttp(orig, repo, req, res, proto, cmd):
365 > def wrappedcallhttp(orig, repo, req, res, proto, cmd):
366 > path = req.advertisedurl[len(req.advertisedbaseurl):]
366 > path = req.advertisedurl[len(req.advertisedbaseurl):]
367 > if not path.startswith(b'/redirector'):
367 > if not path.startswith(b'/redirector'):
368 > return orig(repo, req, res, proto, cmd)
368 > return orig(repo, req, res, proto, cmd)
369 > relpath = path[len(b'/redirector'):]
369 > relpath = path[len(b'/redirector'):]
370 > res.status = b'301 Redirect'
370 > res.status = b'301 Redirect'
371 > newurl = b'%s/redirected%s' % (req.baseurl, relpath)
371 > newurl = b'%s/redirected%s' % (req.baseurl, relpath)
372 > if not repo.ui.configbool('testing', 'redirectqs', True) and b'?' in newurl:
372 > if not repo.ui.configbool('testing', 'redirectqs', True) and b'?' in newurl:
373 > newurl = newurl[0:newurl.index(b'?')]
373 > newurl = newurl[0:newurl.index(b'?')]
374 > res.headers[b'Location'] = newurl
374 > res.headers[b'Location'] = newurl
375 > res.headers[b'Content-Type'] = b'text/plain'
375 > res.headers[b'Content-Type'] = b'text/plain'
376 > res.setbodybytes(b'redirected')
376 > res.setbodybytes(b'redirected')
377 > return True
377 > return True
378 >
378 >
379 > extensions.wrapfunction(wireprotoserver, '_callhttp', wrappedcallhttp)
379 > extensions.wrapfunction(wireprotoserver, '_callhttp', wrappedcallhttp)
380 > EOF
380 > EOF
381
381
382 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
382 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
383 > --config server.compressionengines=zlib \
383 > --config server.compressionengines=zlib \
384 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
384 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
385 $ cat hg.pid > $DAEMON_PIDS
385 $ cat hg.pid > $DAEMON_PIDS
386
386
387 Verify our HTTP 301 is served properly
387 Verify our HTTP 301 is served properly
388
388
389 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
389 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
390 > httprequest GET /redirector?cmd=capabilities
390 > httprequest GET /redirector?cmd=capabilities
391 > user-agent: test
391 > user-agent: test
392 > EOF
392 > EOF
393 using raw connection to peer
393 using raw connection to peer
394 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
394 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
395 s> Accept-Encoding: identity\r\n
395 s> Accept-Encoding: identity\r\n
396 s> user-agent: test\r\n
396 s> user-agent: test\r\n
397 s> host: $LOCALIP:$HGPORT\r\n (glob)
397 s> host: $LOCALIP:$HGPORT\r\n (glob)
398 s> \r\n
398 s> \r\n
399 s> makefile('rb', None)
399 s> makefile('rb', None)
400 s> HTTP/1.1 301 Redirect\r\n
400 s> HTTP/1.1 301 Redirect\r\n
401 s> Server: testing stub value\r\n
401 s> Server: testing stub value\r\n
402 s> Date: $HTTP_DATE$\r\n
402 s> Date: $HTTP_DATE$\r\n
403 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
403 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
404 s> Content-Type: text/plain\r\n
404 s> Content-Type: text/plain\r\n
405 s> Content-Length: 10\r\n
405 s> Content-Length: 10\r\n
406 s> \r\n
406 s> \r\n
407 s> redirected
407 s> redirected
408 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
408 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
409 s> Accept-Encoding: identity\r\n
409 s> Accept-Encoding: identity\r\n
410 s> user-agent: test\r\n
410 s> user-agent: test\r\n
411 s> host: $LOCALIP:$HGPORT\r\n (glob)
411 s> host: $LOCALIP:$HGPORT\r\n (glob)
412 s> \r\n
412 s> \r\n
413 s> makefile('rb', None)
413 s> makefile('rb', None)
414 s> HTTP/1.1 200 Script output follows\r\n
414 s> HTTP/1.1 200 Script output follows\r\n
415 s> Server: testing stub value\r\n
415 s> Server: testing stub value\r\n
416 s> Date: $HTTP_DATE$\r\n
416 s> Date: $HTTP_DATE$\r\n
417 s> Content-Type: application/mercurial-0.1\r\n
417 s> Content-Type: application/mercurial-0.1\r\n
418 s> Content-Length: 453\r\n
418 s> Content-Length: 453\r\n
419 s> \r\n
419 s> \r\n
420 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
420 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
421
421
422 Test with the HTTP peer
422 Test with the HTTP peer
423
423
424 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
424 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
425 > command heads
425 > command heads
426 > EOF
426 > EOF
427 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
427 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
428 s> Accept-Encoding: identity\r\n
428 s> Accept-Encoding: identity\r\n
429 s> accept: application/mercurial-0.1\r\n
429 s> accept: application/mercurial-0.1\r\n
430 s> host: $LOCALIP:$HGPORT\r\n (glob)
430 s> host: $LOCALIP:$HGPORT\r\n (glob)
431 s> user-agent: Mercurial debugwireproto\r\n
431 s> user-agent: Mercurial debugwireproto\r\n
432 s> \r\n
432 s> \r\n
433 s> makefile('rb', None)
433 s> makefile('rb', None)
434 s> HTTP/1.1 301 Redirect\r\n
434 s> HTTP/1.1 301 Redirect\r\n
435 s> Server: testing stub value\r\n
435 s> Server: testing stub value\r\n
436 s> Date: $HTTP_DATE$\r\n
436 s> Date: $HTTP_DATE$\r\n
437 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
437 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
438 s> Content-Type: text/plain\r\n
438 s> Content-Type: text/plain\r\n
439 s> Content-Length: 10\r\n
439 s> Content-Length: 10\r\n
440 s> \r\n
440 s> \r\n
441 s> redirected
441 s> redirected
442 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
442 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
443 s> Accept-Encoding: identity\r\n
443 s> Accept-Encoding: identity\r\n
444 s> accept: application/mercurial-0.1\r\n
444 s> accept: application/mercurial-0.1\r\n
445 s> host: $LOCALIP:$HGPORT\r\n (glob)
445 s> host: $LOCALIP:$HGPORT\r\n (glob)
446 s> user-agent: Mercurial debugwireproto\r\n
446 s> user-agent: Mercurial debugwireproto\r\n
447 s> \r\n
447 s> \r\n
448 s> makefile('rb', None)
448 s> makefile('rb', None)
449 s> HTTP/1.1 200 Script output follows\r\n
449 s> HTTP/1.1 200 Script output follows\r\n
450 s> Server: testing stub value\r\n
450 s> Server: testing stub value\r\n
451 s> Date: $HTTP_DATE$\r\n
451 s> Date: $HTTP_DATE$\r\n
452 s> Content-Type: application/mercurial-0.1\r\n
452 s> Content-Type: application/mercurial-0.1\r\n
453 s> Content-Length: 453\r\n
453 s> Content-Length: 453\r\n
454 s> \r\n
454 s> \r\n
455 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
455 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
456 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
456 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
457 sending heads command
457 sending heads command
458 s> GET /redirected?cmd=heads HTTP/1.1\r\n
458 s> GET /redirected?cmd=heads HTTP/1.1\r\n
459 s> Accept-Encoding: identity\r\n
459 s> Accept-Encoding: identity\r\n
460 s> vary: X-HgProto-1\r\n
460 s> vary: X-HgProto-1\r\n
461 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
461 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
462 s> accept: application/mercurial-0.1\r\n
462 s> accept: application/mercurial-0.1\r\n
463 s> host: $LOCALIP:$HGPORT\r\n (glob)
463 s> host: $LOCALIP:$HGPORT\r\n (glob)
464 s> user-agent: Mercurial debugwireproto\r\n
464 s> user-agent: Mercurial debugwireproto\r\n
465 s> \r\n
465 s> \r\n
466 s> makefile('rb', None)
466 s> makefile('rb', None)
467 s> HTTP/1.1 200 Script output follows\r\n
467 s> HTTP/1.1 200 Script output follows\r\n
468 s> Server: testing stub value\r\n
468 s> Server: testing stub value\r\n
469 s> Date: $HTTP_DATE$\r\n
469 s> Date: $HTTP_DATE$\r\n
470 s> Content-Type: application/mercurial-0.1\r\n
470 s> Content-Type: application/mercurial-0.1\r\n
471 s> Content-Length: 41\r\n
471 s> Content-Length: 41\r\n
472 s> \r\n
472 s> \r\n
473 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
473 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
474 response: [
474 response: [
475 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
475 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
476 ]
476 ]
477
477
478 $ killdaemons.py
478 $ killdaemons.py
479
479
480 Now test a variation where we strip the query string from the redirect URL.
480 Now test a variation where we strip the query string from the redirect URL.
481 (SCM Manager apparently did this and clients would recover from it)
481 (SCM Manager apparently did this and clients would recover from it)
482
482
483 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
483 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
484 > --config server.compressionengines=zlib \
484 > --config server.compressionengines=zlib \
485 > --config testing.redirectqs=false \
485 > --config testing.redirectqs=false \
486 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
486 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
487 $ cat hg.pid > $DAEMON_PIDS
487 $ cat hg.pid > $DAEMON_PIDS
488
488
489 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
489 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
490 > httprequest GET /redirector?cmd=capabilities
490 > httprequest GET /redirector?cmd=capabilities
491 > user-agent: test
491 > user-agent: test
492 > EOF
492 > EOF
493 using raw connection to peer
493 using raw connection to peer
494 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
494 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
495 s> Accept-Encoding: identity\r\n
495 s> Accept-Encoding: identity\r\n
496 s> user-agent: test\r\n
496 s> user-agent: test\r\n
497 s> host: $LOCALIP:$HGPORT\r\n (glob)
497 s> host: $LOCALIP:$HGPORT\r\n (glob)
498 s> \r\n
498 s> \r\n
499 s> makefile('rb', None)
499 s> makefile('rb', None)
500 s> HTTP/1.1 301 Redirect\r\n
500 s> HTTP/1.1 301 Redirect\r\n
501 s> Server: testing stub value\r\n
501 s> Server: testing stub value\r\n
502 s> Date: $HTTP_DATE$\r\n
502 s> Date: $HTTP_DATE$\r\n
503 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
503 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
504 s> Content-Type: text/plain\r\n
504 s> Content-Type: text/plain\r\n
505 s> Content-Length: 10\r\n
505 s> Content-Length: 10\r\n
506 s> \r\n
506 s> \r\n
507 s> redirected
507 s> redirected
508 s> GET /redirected HTTP/1.1\r\n
508 s> GET /redirected HTTP/1.1\r\n
509 s> Accept-Encoding: identity\r\n
509 s> Accept-Encoding: identity\r\n
510 s> user-agent: test\r\n
510 s> user-agent: test\r\n
511 s> host: $LOCALIP:$HGPORT\r\n (glob)
511 s> host: $LOCALIP:$HGPORT\r\n (glob)
512 s> \r\n
512 s> \r\n
513 s> makefile('rb', None)
513 s> makefile('rb', None)
514 s> HTTP/1.1 200 Script output follows\r\n
514 s> HTTP/1.1 200 Script output follows\r\n
515 s> Server: testing stub value\r\n
515 s> Server: testing stub value\r\n
516 s> Date: $HTTP_DATE$\r\n
516 s> Date: $HTTP_DATE$\r\n
517 s> ETag: W/"*"\r\n (glob)
517 s> ETag: W/"*"\r\n (glob)
518 s> Content-Type: text/html; charset=ascii\r\n
518 s> Content-Type: text/html; charset=ascii\r\n
519 s> Transfer-Encoding: chunked\r\n
519 s> Transfer-Encoding: chunked\r\n
520 s> \r\n
520 s> \r\n
521 s> 414\r\n
521 s> 414\r\n
522 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
522 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
523 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
523 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
524 s> <head>\n
524 s> <head>\n
525 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
525 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
526 s> <meta name="robots" content="index, nofollow" />\n
526 s> <meta name="robots" content="index, nofollow" />\n
527 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
527 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
528 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
528 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
529 s> \n
529 s> \n
530 s> <title>redirected: log</title>\n
530 s> <title>redirected: log</title>\n
531 s> <link rel="alternate" type="application/atom+xml"\n
531 s> <link rel="alternate" type="application/atom+xml"\n
532 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
532 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
533 s> <link rel="alternate" type="application/rss+xml"\n
533 s> <link rel="alternate" type="application/rss+xml"\n
534 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
534 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
535 s> </head>\n
535 s> </head>\n
536 s> <body>\n
536 s> <body>\n
537 s> \n
537 s> \n
538 s> <div class="container">\n
538 s> <div class="container">\n
539 s> <div class="menu">\n
539 s> <div class="menu">\n
540 s> <div class="logo">\n
540 s> <div class="logo">\n
541 s> <a href="https://mercurial-scm.org/">\n
541 s> <a href="https://mercurial-scm.org/">\n
542 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
542 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
543 s> </div>\n
543 s> </div>\n
544 s> <ul>\n
544 s> <ul>\n
545 s> <li class="active">log</li>\n
545 s> <li class="active">log</li>\n
546 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
546 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
547 s> <li><a href="/redirected/tags">tags</a></li>\n
547 s> <li><a href="/redirected/tags">tags</a></li>\n
548 s> <li><a href="
548 s> <li><a href="
549 s> \r\n
549 s> \r\n
550 s> 810\r\n
550 s> 810\r\n
551 s> /redirected/bookmarks">bookmarks</a></li>\n
551 s> /redirected/bookmarks">bookmarks</a></li>\n
552 s> <li><a href="/redirected/branches">branches</a></li>\n
552 s> <li><a href="/redirected/branches">branches</a></li>\n
553 s> </ul>\n
553 s> </ul>\n
554 s> <ul>\n
554 s> <ul>\n
555 s> <li><a href="/redirected/rev/tip">changeset</a></li>\n
555 s> <li><a href="/redirected/rev/tip">changeset</a></li>\n
556 s> <li><a href="/redirected/file/tip">browse</a></li>\n
556 s> <li><a href="/redirected/file/tip">browse</a></li>\n
557 s> </ul>\n
557 s> </ul>\n
558 s> <ul>\n
558 s> <ul>\n
559 s> \n
559 s> \n
560 s> </ul>\n
560 s> </ul>\n
561 s> <ul>\n
561 s> <ul>\n
562 s> <li><a href="/redirected/help">help</a></li>\n
562 s> <li><a href="/redirected/help">help</a></li>\n
563 s> </ul>\n
563 s> </ul>\n
564 s> <div class="atom-logo">\n
564 s> <div class="atom-logo">\n
565 s> <a href="/redirected/atom-log" title="subscribe to atom feed">\n
565 s> <a href="/redirected/atom-log" title="subscribe to atom feed">\n
566 s> <img class="atom-logo" src="/redirected/static/feed-icon-14x14.png" alt="atom feed" />\n
566 s> <img class="atom-logo" src="/redirected/static/feed-icon-14x14.png" alt="atom feed" />\n
567 s> </a>\n
567 s> </a>\n
568 s> </div>\n
568 s> </div>\n
569 s> </div>\n
569 s> </div>\n
570 s> \n
570 s> \n
571 s> <div class="main">\n
571 s> <div class="main">\n
572 s> <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/redirected">redirected</a> </h2>\n
572 s> <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/redirected">redirected</a> </h2>\n
573 s> <h3>log</h3>\n
573 s> <h3>log</h3>\n
574 s> \n
574 s> \n
575 s> \n
575 s> \n
576 s> <form class="search" action="/redirected/log">\n
576 s> <form class="search" action="/redirected/log">\n
577 s> \n
577 s> \n
578 s> <p><input name="rev" id="search1" type="text" size="30" value="" /></p>\n
578 s> <p><input name="rev" id="search1" type="text" size="30" value="" /></p>\n
579 s> <div id="hint">Find changesets by keywords (author, files, the commit message), revision\n
579 s> <div id="hint">Find changesets by keywords (author, files, the commit message), revision\n
580 s> number or hash, or <a href="/redirected/help/revsets">revset expression</a>.</div>\n
580 s> number or hash, or <a href="/redirected/help/revsets">revset expression</a>.</div>\n
581 s> </form>\n
581 s> </form>\n
582 s> \n
582 s> \n
583 s> <div class="navigate">\n
583 s> <div class="navigate">\n
584 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
584 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
585 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
585 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
586 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
586 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
587 s> </div>\n
587 s> </div>\n
588 s> \n
588 s> \n
589 s> <table class="bigtable">\n
589 s> <table class="bigtable">\n
590 s> <thead>\n
590 s> <thead>\n
591 s> <tr>\n
591 s> <tr>\n
592 s> <th class="age">age</th>\n
592 s> <th class="age">age</th>\n
593 s> <th class="author">author</th>\n
593 s> <th class="author">author</th>\n
594 s> <th class="description">description</th>\n
594 s> <th class="description">description</th>\n
595 s> </tr>\n
595 s> </tr>\n
596 s> </thead>\n
596 s> </thead>\n
597 s> <tbody class="stripes2">\n
597 s> <tbody class="stripes2">\n
598 s> <tr>\n
598 s> <tr>\n
599 s> <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>\n
599 s> <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>\n
600 s> <td class="author">test</td>\n
600 s> <td class="author">test</td>\n
601 s> <td class="description">\n
601 s> <td class="description">\n
602 s> <a href="/redirected/rev/96ee1d7354c4">initial</a>\n
602 s> <a href="/redirected/rev/96ee1d7354c4">initial</a>\n
603 s> <span class="phase">draft</span> <span class="branchhead">default</span> <span class="tag">tip</span> \n
603 s> <span class="phase">draft</span> <span class="branchhead">default</span> <span class="tag">tip</span> \n
604 s> </td>\n
604 s> </td>\n
605 s> </tr>\n
605 s> </tr>\n
606 s> \n
606 s> \n
607 s> </tbody>\n
607 s> </tbody>\n
608 s> </table>\n
608 s> </table>\n
609 s> \n
609 s> \n
610 s> <div class="navigate">\n
610 s> <div class="navigate">\n
611 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
611 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
612 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
612 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
613 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
613 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
614 s> </div>\n
614 s> </div>\n
615 s> \n
615 s> \n
616 s> <script type="text/javascript">\n
616 s> <script type="text/javascript">\n
617 s> ajaxScrollInit(\n
617 s> ajaxScrollInit(\n
618 s> \'/redirected/shortlog/%next%\',\n
618 s> \'/redirected/shortlog/%next%\',\n
619 s> \'\', <!-- NEXTHASH\n
619 s> \'\', <!-- NEXTHASH\n
620 s> function (htmlText) {
620 s> function (htmlText) {
621 s> \r\n
621 s> \r\n
622 s> 14a\r\n
622 s> 14a\r\n
623 s> \n
623 s> \n
624 s> var m = htmlText.match(/\'(\\w+)\', <!-- NEXTHASH/);\n
624 s> var m = htmlText.match(/\'(\\w+)\', <!-- NEXTHASH/);\n
625 s> return m ? m[1] : null;\n
625 s> return m ? m[1] : null;\n
626 s> },\n
626 s> },\n
627 s> \'.bigtable > tbody\',\n
627 s> \'.bigtable > tbody\',\n
628 s> \'<tr class="%class%">\\\n
628 s> \'<tr class="%class%">\\\n
629 s> <td colspan="3" style="text-align: center;">%text%</td>\\\n
629 s> <td colspan="3" style="text-align: center;">%text%</td>\\\n
630 s> </tr>\'\n
630 s> </tr>\'\n
631 s> );\n
631 s> );\n
632 s> </script>\n
632 s> </script>\n
633 s> \n
633 s> \n
634 s> </div>\n
634 s> </div>\n
635 s> </div>\n
635 s> </div>\n
636 s> \n
636 s> \n
637 s> \n
637 s> \n
638 s> \n
638 s> \n
639 s> </body>\n
639 s> </body>\n
640 s> </html>\n
640 s> </html>\n
641 s> \n
641 s> \n
642 s> \r\n
642 s> \r\n
643 s> 0\r\n
643 s> 0\r\n
644 s> \r\n
644 s> \r\n
645
645
646 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
646 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
647 > command heads
647 > command heads
648 > EOF
648 > EOF
649 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
649 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
650 s> Accept-Encoding: identity\r\n
650 s> Accept-Encoding: identity\r\n
651 s> accept: application/mercurial-0.1\r\n
651 s> accept: application/mercurial-0.1\r\n
652 s> host: $LOCALIP:$HGPORT\r\n (glob)
652 s> host: $LOCALIP:$HGPORT\r\n (glob)
653 s> user-agent: Mercurial debugwireproto\r\n
653 s> user-agent: Mercurial debugwireproto\r\n
654 s> \r\n
654 s> \r\n
655 s> makefile('rb', None)
655 s> makefile('rb', None)
656 s> HTTP/1.1 301 Redirect\r\n
656 s> HTTP/1.1 301 Redirect\r\n
657 s> Server: testing stub value\r\n
657 s> Server: testing stub value\r\n
658 s> Date: $HTTP_DATE$\r\n
658 s> Date: $HTTP_DATE$\r\n
659 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
659 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
660 s> Content-Type: text/plain\r\n
660 s> Content-Type: text/plain\r\n
661 s> Content-Length: 10\r\n
661 s> Content-Length: 10\r\n
662 s> \r\n
662 s> \r\n
663 s> redirected
663 s> redirected
664 s> GET /redirected HTTP/1.1\r\n
664 s> GET /redirected HTTP/1.1\r\n
665 s> Accept-Encoding: identity\r\n
665 s> Accept-Encoding: identity\r\n
666 s> accept: application/mercurial-0.1\r\n
666 s> accept: application/mercurial-0.1\r\n
667 s> host: $LOCALIP:$HGPORT\r\n (glob)
667 s> host: $LOCALIP:$HGPORT\r\n (glob)
668 s> user-agent: Mercurial debugwireproto\r\n
668 s> user-agent: Mercurial debugwireproto\r\n
669 s> \r\n
669 s> \r\n
670 s> makefile('rb', None)
670 s> makefile('rb', None)
671 s> HTTP/1.1 200 Script output follows\r\n
671 s> HTTP/1.1 200 Script output follows\r\n
672 s> Server: testing stub value\r\n
672 s> Server: testing stub value\r\n
673 s> Date: $HTTP_DATE$\r\n
673 s> Date: $HTTP_DATE$\r\n
674 s> ETag: W/"*"\r\n (glob)
674 s> ETag: W/"*"\r\n (glob)
675 s> Content-Type: text/html; charset=ascii\r\n
675 s> Content-Type: text/html; charset=ascii\r\n
676 s> Transfer-Encoding: chunked\r\n
676 s> Transfer-Encoding: chunked\r\n
677 s> \r\n
677 s> \r\n
678 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
678 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
679 s> 414\r\n
679 s> 414\r\n
680 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
680 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
681 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
681 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
682 s> <head>\n
682 s> <head>\n
683 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
683 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
684 s> <meta name="robots" content="index, nofollow" />\n
684 s> <meta name="robots" content="index, nofollow" />\n
685 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
685 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
686 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
686 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
687 s> \n
687 s> \n
688 s> <title>redirected: log</title>\n
688 s> <title>redirected: log</title>\n
689 s> <link rel="alternate" type="application/atom+xml"\n
689 s> <link rel="alternate" type="application/atom+xml"\n
690 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
690 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
691 s> <link rel="alternate" type="application/rss+xml"\n
691 s> <link rel="alternate" type="application/rss+xml"\n
692 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
692 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
693 s> </head>\n
693 s> </head>\n
694 s> <body>\n
694 s> <body>\n
695 s> \n
695 s> \n
696 s> <div class="container">\n
696 s> <div class="container">\n
697 s> <div class="menu">\n
697 s> <div class="menu">\n
698 s> <div class="logo">\n
698 s> <div class="logo">\n
699 s> <a href="https://mercurial-scm.org/">\n
699 s> <a href="https://mercurial-scm.org/">\n
700 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
700 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
701 s> </div>\n
701 s> </div>\n
702 s> <ul>\n
702 s> <ul>\n
703 s> <li class="active">log</li>\n
703 s> <li class="active">log</li>\n
704 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
704 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
705 s> <li><a href="/redirected/tags">tags</a
705 s> <li><a href="/redirected/tags">tags</a
706 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
706 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
707 s> Accept-Encoding: identity\r\n
707 s> Accept-Encoding: identity\r\n
708 s> accept: application/mercurial-0.1\r\n
708 s> accept: application/mercurial-0.1\r\n
709 s> host: $LOCALIP:$HGPORT\r\n (glob)
709 s> host: $LOCALIP:$HGPORT\r\n (glob)
710 s> user-agent: Mercurial debugwireproto\r\n
710 s> user-agent: Mercurial debugwireproto\r\n
711 s> \r\n
711 s> \r\n
712 s> makefile('rb', None)
712 s> makefile('rb', None)
713 s> HTTP/1.1 200 Script output follows\r\n
713 s> HTTP/1.1 200 Script output follows\r\n
714 s> Server: testing stub value\r\n
714 s> Server: testing stub value\r\n
715 s> Date: $HTTP_DATE$\r\n
715 s> Date: $HTTP_DATE$\r\n
716 s> Content-Type: application/mercurial-0.1\r\n
716 s> Content-Type: application/mercurial-0.1\r\n
717 s> Content-Length: 453\r\n
717 s> Content-Length: 453\r\n
718 s> \r\n
718 s> \r\n
719 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
719 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
720 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
720 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
721 sending heads command
721 sending heads command
722 s> GET /redirected?cmd=heads HTTP/1.1\r\n
722 s> GET /redirected?cmd=heads HTTP/1.1\r\n
723 s> Accept-Encoding: identity\r\n
723 s> Accept-Encoding: identity\r\n
724 s> vary: X-HgProto-1\r\n
724 s> vary: X-HgProto-1\r\n
725 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
725 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
726 s> accept: application/mercurial-0.1\r\n
726 s> accept: application/mercurial-0.1\r\n
727 s> host: $LOCALIP:$HGPORT\r\n (glob)
727 s> host: $LOCALIP:$HGPORT\r\n (glob)
728 s> user-agent: Mercurial debugwireproto\r\n
728 s> user-agent: Mercurial debugwireproto\r\n
729 s> \r\n
729 s> \r\n
730 s> makefile('rb', None)
730 s> makefile('rb', None)
731 s> HTTP/1.1 200 Script output follows\r\n
731 s> HTTP/1.1 200 Script output follows\r\n
732 s> Server: testing stub value\r\n
732 s> Server: testing stub value\r\n
733 s> Date: $HTTP_DATE$\r\n
733 s> Date: $HTTP_DATE$\r\n
734 s> Content-Type: application/mercurial-0.1\r\n
734 s> Content-Type: application/mercurial-0.1\r\n
735 s> Content-Length: 41\r\n
735 s> Content-Length: 41\r\n
736 s> \r\n
736 s> \r\n
737 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
737 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
738 response: [
738 response: [
739 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
739 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
740 ]
740 ]
@@ -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> \xa3Dapis\xa0GapibaseDapi/Nv1capabilitiesY\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> \xa3Dapis\xa0GapibaseDapi/Nv1capabilitiesY\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> \xa3Dapis\xa1Pexp-http-v2-0001\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-0005GapibaseDapi/Nv1capabilitiesY\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> \xa3Dapis\xa1Pexp-http-v2-0001\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-0005GapibaseDapi/Nv1capabilitiesY\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\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
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
General Comments 0
You need to be logged in to leave comments. Login now