##// END OF EJS Templates
wireproto: implement basic command dispatching for HTTPv2...
Gregory Szorc -
r37072:e7a012b6 default
parent child Browse files
Show More
@@ -360,10 +360,7 b' def _handlehttpv2request(rctx, req, res,'
360 'value: %s\n') % FRAMINGTYPE)
360 'value: %s\n') % FRAMINGTYPE)
361 return
361 return
362
362
363 # We don't do anything meaningful yet.
363 _processhttpv2request(ui, repo, req, res, permission, command, proto)
364 res.status = b'200 OK'
365 res.headers[b'Content-Type'] = b'text/plain'
366 res.setbodybytes(b'/'.join(urlparts) + b'\n')
367
364
368 def _processhttpv2reflectrequest(ui, repo, req, res):
365 def _processhttpv2reflectrequest(ui, repo, req, res):
369 """Reads unified frame protocol request and dumps out state to client.
366 """Reads unified frame protocol request and dumps out state to client.
@@ -408,6 +405,104 b' def _processhttpv2reflectrequest(ui, rep'
408 res.headers[b'Content-Type'] = b'text/plain'
405 res.headers[b'Content-Type'] = b'text/plain'
409 res.setbodybytes(b'\n'.join(states))
406 res.setbodybytes(b'\n'.join(states))
410
407
408 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
409 """Post-validation handler for HTTPv2 requests.
410
411 Called when the HTTP request contains unified frame-based protocol
412 frames for evaluation.
413 """
414 reactor = wireprotoframing.serverreactor()
415 seencommand = False
416
417 while True:
418 frame = wireprotoframing.readframe(req.bodyfh)
419 if not frame:
420 break
421
422 action, meta = reactor.onframerecv(*frame)
423
424 if action == 'wantframe':
425 # Need more data before we can do anything.
426 continue
427 elif action == 'runcommand':
428 # We currently only support running a single command per
429 # HTTP request.
430 if seencommand:
431 # TODO define proper error mechanism.
432 res.status = b'200 OK'
433 res.headers[b'Content-Type'] = b'text/plain'
434 res.setbodybytes(_('support for multiple commands per request '
435 'not yet implemented'))
436 return
437
438 _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand,
439 reactor, meta)
440
441 elif action == 'error':
442 # TODO define proper error mechanism.
443 res.status = b'200 OK'
444 res.headers[b'Content-Type'] = b'text/plain'
445 res.setbodybytes(meta['message'] + b'\n')
446 return
447 else:
448 raise error.ProgrammingError(
449 'unhandled action from frame processor: %s' % action)
450
451 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
452 command):
453 """Dispatch a wire protocol command made from HTTPv2 requests.
454
455 The authenticated permission (``authedperm``) along with the original
456 command from the URL (``reqcommand``) are passed in.
457 """
458 # We already validated that the session has permissions to perform the
459 # actions in ``authedperm``. In the unified frame protocol, the canonical
460 # command to run is expressed in a frame. However, the URL also requested
461 # to run a specific command. We need to be careful that the command we
462 # run doesn't have permissions requirements greater than what was granted
463 # by ``authedperm``.
464 #
465 # For now, this is no big deal, as we only allow a single command per
466 # request and that command must match the command in the URL. But when
467 # things change, we need to watch out...
468 if reqcommand != command['command']:
469 # TODO define proper error mechanism
470 res.status = b'200 OK'
471 res.headers[b'Content-Type'] = b'text/plain'
472 res.setbodybytes(_('command in frame must match command in URL'))
473 return
474
475 # TODO once we get rid of the command==URL restriction, we'll need to
476 # revalidate command validity and auth here. checkperm,
477 # wireproto.commands.commandavailable(), etc.
478
479 proto = httpv2protocolhandler(req, ui, args=command['args'])
480 assert wireproto.commands.commandavailable(command['command'], proto)
481 wirecommand = wireproto.commands[command['command']]
482
483 assert authedperm in (b'ro', b'rw')
484 assert wirecommand.permission in ('push', 'pull')
485
486 # We already checked this as part of the URL==command check, but
487 # permissions are important, so do it again.
488 if authedperm == b'ro':
489 assert wirecommand.permission == 'pull'
490 elif authedperm == b'rw':
491 # We are allowed to access read-only commands under the rw URL.
492 assert wirecommand.permission in ('push', 'pull')
493
494 rsp = wireproto.dispatch(repo, proto, command['command'])
495
496 # TODO use proper response format.
497 res.status = b'200 OK'
498 res.headers[b'Content-Type'] = b'text/plain'
499
500 if isinstance(rsp, wireprototypes.bytesresponse):
501 res.setbodybytes(rsp.data)
502 else:
503 res.setbodybytes(b'unhandled response type from wire proto '
504 'command')
505
411 # Maps API name to metadata so custom API can be registered.
506 # Maps API name to metadata so custom API can be registered.
412 API_HANDLERS = {
507 API_HANDLERS = {
413 HTTPV2: {
508 HTTPV2: {
@@ -417,16 +512,24 b' API_HANDLERS = {'
417 }
512 }
418
513
419 class httpv2protocolhandler(wireprototypes.baseprotocolhandler):
514 class httpv2protocolhandler(wireprototypes.baseprotocolhandler):
420 def __init__(self, req, ui):
515 def __init__(self, req, ui, args=None):
421 self._req = req
516 self._req = req
422 self._ui = ui
517 self._ui = ui
518 self._args = args
423
519
424 @property
520 @property
425 def name(self):
521 def name(self):
426 return HTTPV2
522 return HTTPV2
427
523
428 def getargs(self, args):
524 def getargs(self, args):
429 raise NotImplementedError
525 data = {}
526 for k in args.split():
527 if k == '*':
528 raise NotImplementedError('do not support * args')
529 else:
530 data[k] = self._args[k]
531
532 return [data[k] for k in args.split()]
430
533
431 def forwardpayload(self, fp):
534 def forwardpayload(self, fp):
432 raise NotImplementedError
535 raise NotImplementedError
@@ -439,7 +542,7 b' class httpv2protocolhandler(wireprototyp'
439 raise NotImplementedError
542 raise NotImplementedError
440
543
441 def addcapabilities(self, repo, caps):
544 def addcapabilities(self, repo, caps):
442 raise NotImplementedError
545 return caps
443
546
444 def checkperm(self, perm):
547 def checkperm(self, perm):
445 raise NotImplementedError
548 raise NotImplementedError
@@ -196,9 +196,9 b' Request to read-only command works out o'
196 s> Server: testing stub value\r\n
196 s> Server: testing stub value\r\n
197 s> Date: $HTTP_DATE$\r\n
197 s> Date: $HTTP_DATE$\r\n
198 s> Content-Type: text/plain\r\n
198 s> Content-Type: text/plain\r\n
199 s> Content-Length: 18\r\n
199 s> Content-Length: 29\r\n
200 s> \r\n
200 s> \r\n
201 s> ro/customreadonly\n
201 s> customreadonly bytes response
202
202
203 Request to read-write command fails because server is read-only by default
203 Request to read-write command fails because server is read-only by default
204
204
@@ -303,9 +303,9 b' Authorized request for valid read-write '
303 s> Server: testing stub value\r\n
303 s> Server: testing stub value\r\n
304 s> Date: $HTTP_DATE$\r\n
304 s> Date: $HTTP_DATE$\r\n
305 s> Content-Type: text/plain\r\n
305 s> Content-Type: text/plain\r\n
306 s> Content-Length: 18\r\n
306 s> Content-Length: 29\r\n
307 s> \r\n
307 s> \r\n
308 s> rw/customreadonly\n
308 s> customreadonly bytes response
309
309
310 Authorized request for unknown command is rejected
310 Authorized request for unknown command is rejected
311
311
General Comments 0
You need to be logged in to leave comments. Login now