270 |
270 |
271 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm, |
271 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm, |
272 req.dispatchparts[2:]) |
272 req.dispatchparts[2:]) |
273 |
273 |
274 def _handlehttpv2request(rctx, req, res, checkperm, urlparts): |
274 def _handlehttpv2request(rctx, req, res, checkperm, urlparts): |
|
275 from .hgweb import common as hgwebcommon |
|
276 |
|
277 # URL space looks like: <permissions>/<command>, where <permission> can |
|
278 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively. |
|
279 |
|
280 # Root URL does nothing meaningful... yet. |
|
281 if not urlparts: |
|
282 res.status = b'200 OK' |
|
283 res.headers[b'Content-Type'] = b'text/plain' |
|
284 res.setbodybytes(_('HTTP version 2 API handler')) |
|
285 return |
|
286 |
|
287 if len(urlparts) == 1: |
|
288 res.status = b'404 Not Found' |
|
289 res.headers[b'Content-Type'] = b'text/plain' |
|
290 res.setbodybytes(_('do not know how to process %s\n') % |
|
291 req.dispatchpath) |
|
292 return |
|
293 |
|
294 permission, command = urlparts[0:2] |
|
295 |
|
296 if permission not in (b'ro', b'rw'): |
|
297 res.status = b'404 Not Found' |
|
298 res.headers[b'Content-Type'] = b'text/plain' |
|
299 res.setbodybytes(_('unknown permission: %s') % permission) |
|
300 return |
|
301 |
|
302 # At some point we'll want to use our own API instead of recycling the |
|
303 # behavior of version 1 of the wire protocol... |
|
304 # TODO return reasonable responses - not responses that overload the |
|
305 # HTTP status line message for error reporting. |
|
306 try: |
|
307 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push') |
|
308 except hgwebcommon.ErrorResponse as e: |
|
309 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e)) |
|
310 for k, v in e.headers: |
|
311 res.headers[k] = v |
|
312 res.setbodybytes('permission denied') |
|
313 return |
|
314 |
|
315 if command not in wireproto.commands: |
|
316 res.status = b'404 Not Found' |
|
317 res.headers[b'Content-Type'] = b'text/plain' |
|
318 res.setbodybytes(_('unknown wire protocol command: %s\n') % command) |
|
319 return |
|
320 |
|
321 repo = rctx.repo |
|
322 ui = repo.ui |
|
323 |
|
324 proto = httpv2protocolhandler(req, ui) |
|
325 |
|
326 if not wireproto.commands.commandavailable(command, proto): |
|
327 res.status = b'404 Not Found' |
|
328 res.headers[b'Content-Type'] = b'text/plain' |
|
329 res.setbodybytes(_('invalid wire protocol command: %s') % command) |
|
330 return |
|
331 |
|
332 # We don't do anything meaningful yet. |
275 res.status = b'200 OK' |
333 res.status = b'200 OK' |
276 res.headers[b'Content-Type'] = b'text/plain' |
334 res.headers[b'Content-Type'] = b'text/plain' |
277 res.setbodybytes(b'/'.join(urlparts) + b'\n') |
335 res.setbodybytes(b'/'.join(urlparts) + b'\n') |
278 |
336 |
279 # Maps API name to metadata so custom API can be registered. |
337 # Maps API name to metadata so custom API can be registered. |
281 HTTPV2: { |
339 HTTPV2: { |
282 'config': ('experimental', 'web.api.http-v2'), |
340 'config': ('experimental', 'web.api.http-v2'), |
283 'handler': _handlehttpv2request, |
341 'handler': _handlehttpv2request, |
284 }, |
342 }, |
285 } |
343 } |
|
344 |
|
345 class httpv2protocolhandler(wireprototypes.baseprotocolhandler): |
|
346 def __init__(self, req, ui): |
|
347 self._req = req |
|
348 self._ui = ui |
|
349 |
|
350 @property |
|
351 def name(self): |
|
352 return HTTPV2 |
|
353 |
|
354 def getargs(self, args): |
|
355 raise NotImplementedError |
|
356 |
|
357 def forwardpayload(self, fp): |
|
358 raise NotImplementedError |
|
359 |
|
360 @contextlib.contextmanager |
|
361 def mayberedirectstdio(self): |
|
362 raise NotImplementedError |
|
363 |
|
364 def client(self): |
|
365 raise NotImplementedError |
|
366 |
|
367 def addcapabilities(self, repo, caps): |
|
368 raise NotImplementedError |
|
369 |
|
370 def checkperm(self, perm): |
|
371 raise NotImplementedError |
286 |
372 |
287 def _httpresponsetype(ui, req, prefer_uncompressed): |
373 def _httpresponsetype(ui, req, prefer_uncompressed): |
288 """Determine the appropriate response type and compression settings. |
374 """Determine the appropriate response type and compression settings. |
289 |
375 |
290 Returns a tuple of (mediatype, compengine, engineopts). |
376 Returns a tuple of (mediatype, compengine, engineopts). |