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: |
|
199 | s> Content-Length: 29\r\n | |
200 | s> \r\n |
|
200 | s> \r\n | |
201 |
s> |
|
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: |
|
306 | s> Content-Length: 29\r\n | |
307 | s> \r\n |
|
307 | s> \r\n | |
308 |
s> |
|
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