diff -r a708e1e4d7a8 -r 1cfef5693203 mercurial/wireprotoserver.py --- a/mercurial/wireprotoserver.py Tue Mar 13 10:34:36 2018 -0700 +++ b/mercurial/wireprotoserver.py Tue Mar 13 16:53:21 2018 -0700 @@ -33,6 +33,7 @@ HGTYPE2 = 'application/mercurial-0.2' HGERRTYPE = 'application/hg-error' +HTTPV2 = wireprototypes.HTTPV2 SSHV1 = wireprototypes.SSHV1 SSHV2 = wireprototypes.SSHV2 @@ -214,6 +215,75 @@ return True +def handlewsgiapirequest(rctx, req, res, checkperm): + """Handle requests to /api/*.""" + assert req.dispatchparts[0] == b'api' + + repo = rctx.repo + + # This whole URL space is experimental for now. But we want to + # reserve the URL space. So, 404 all URLs if the feature isn't enabled. + if not repo.ui.configbool('experimental', 'web.apiserver'): + res.status = b'404 Not Found' + res.headers[b'Content-Type'] = b'text/plain' + res.setbodybytes(_('Experimental API server endpoint not enabled')) + return + + # The URL space is /api//*. The structure of URLs under varies + # by . + + # Registered APIs are made available via config options of the name of + # the protocol. + availableapis = set() + for k, v in API_HANDLERS.items(): + section, option = v['config'] + if repo.ui.configbool(section, option): + availableapis.add(k) + + # Requests to /api/ list available APIs. + if req.dispatchparts == [b'api']: + res.status = b'200 OK' + res.headers[b'Content-Type'] = b'text/plain' + lines = [_('APIs can be accessed at /api/, where can be ' + 'one of the following:\n')] + if availableapis: + lines.extend(sorted(availableapis)) + else: + lines.append(_('(no available APIs)\n')) + res.setbodybytes(b'\n'.join(lines)) + return + + proto = req.dispatchparts[1] + + if proto not in API_HANDLERS: + res.status = b'404 Not Found' + res.headers[b'Content-Type'] = b'text/plain' + res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % ( + proto, b', '.join(sorted(availableapis)))) + return + + if proto not in availableapis: + res.status = b'404 Not Found' + res.headers[b'Content-Type'] = b'text/plain' + res.setbodybytes(_('API %s not enabled\n') % proto) + return + + API_HANDLERS[proto]['handler'](rctx, req, res, checkperm, + req.dispatchparts[2:]) + +def _handlehttpv2request(rctx, req, res, checkperm, urlparts): + res.status = b'200 OK' + res.headers[b'Content-Type'] = b'text/plain' + res.setbodybytes(b'/'.join(urlparts) + b'\n') + +# Maps API name to metadata so custom API can be registered. +API_HANDLERS = { + HTTPV2: { + 'config': ('experimental', 'web.api.http-v2'), + 'handler': _handlehttpv2request, + }, +} + def _httpresponsetype(ui, req, prefer_uncompressed): """Determine the appropriate response type and compression settings.