Show More
@@ -2631,8 +2631,8 b' def debugwireproto(ui, repo, path=None, ' | |||
|
2631 | 2631 | |
|
2632 | 2632 | ``--peer`` can be used to bypass the handshake protocol and construct a |
|
2633 | 2633 | peer instance using the specified class type. Valid values are ``raw``, |
|
2634 |
``ssh1``, and ``ssh2``. ``raw`` instances only allow sending |
|
|
2635 | payloads and don't support higher-level command actions. | |
|
2634 | ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending | |
|
2635 | raw data payloads and don't support higher-level command actions. | |
|
2636 | 2636 | |
|
2637 | 2637 | ``--noreadstderr`` can be used to disable automatic reading from stderr |
|
2638 | 2638 | of the peer (for SSH connections only). Disabling automatic reading of |
@@ -2678,8 +2678,10 b' def debugwireproto(ui, repo, path=None, ' | |||
|
2678 | 2678 | command listkeys |
|
2679 | 2679 | namespace bookmarks |
|
2680 | 2680 | |
|
2681 | Values are interpreted as Python b'' literals. This allows encoding | |
|
2682 | special byte sequences via backslash escaping. | |
|
2681 | If the value begins with ``eval:``, it will be interpreted as a Python | |
|
2682 | literal expression. Otherwise values are interpreted as Python b'' literals. | |
|
2683 | This allows sending complex types and encoding special byte sequences via | |
|
2684 | backslash escaping. | |
|
2683 | 2685 | |
|
2684 | 2686 | The following arguments have special meaning: |
|
2685 | 2687 | |
@@ -2803,7 +2805,7 b' def debugwireproto(ui, repo, path=None, ' | |||
|
2803 | 2805 | if opts['localssh'] and not repo: |
|
2804 | 2806 | raise error.Abort(_('--localssh requires a repository')) |
|
2805 | 2807 | |
|
2806 | if opts['peer'] and opts['peer'] not in ('raw', 'ssh1', 'ssh2'): | |
|
2808 | if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'): | |
|
2807 | 2809 | raise error.Abort(_('invalid value for --peer'), |
|
2808 | 2810 | hint=_('valid values are "raw", "ssh1", and "ssh2"')) |
|
2809 | 2811 | |
@@ -2877,18 +2879,20 b' def debugwireproto(ui, repo, path=None, ' | |||
|
2877 | 2879 | raise error.Abort(_('only http:// paths are currently supported')) |
|
2878 | 2880 | |
|
2879 | 2881 | url, authinfo = u.authinfo() |
|
2880 |
openerargs = { |
|
|
2882 | openerargs = { | |
|
2883 | r'useragent': b'Mercurial debugwireproto', | |
|
2884 | } | |
|
2881 | 2885 | |
|
2882 | 2886 | # Turn pipes/sockets into observers so we can log I/O. |
|
2883 | 2887 | if ui.verbose: |
|
2884 |
openerargs |
|
|
2888 | openerargs.update({ | |
|
2885 | 2889 | r'loggingfh': ui, |
|
2886 | 2890 | r'loggingname': b's', |
|
2887 | 2891 | r'loggingopts': { |
|
2888 | 2892 | r'logdata': True, |
|
2889 | 2893 | r'logdataapis': False, |
|
2890 | 2894 | }, |
|
2891 | } | |
|
2895 | }) | |
|
2892 | 2896 | |
|
2893 | 2897 | if ui.debugflag: |
|
2894 | 2898 | openerargs[r'loggingopts'][r'logdataapis'] = True |
@@ -2901,7 +2905,10 b' def debugwireproto(ui, repo, path=None, ' | |||
|
2901 | 2905 | |
|
2902 | 2906 | opener = urlmod.opener(ui, authinfo, **openerargs) |
|
2903 | 2907 | |
|
2904 |
if opts['peer'] == ' |
|
|
2908 | if opts['peer'] == 'http2': | |
|
2909 | ui.write(_('creating http peer for wire protocol version 2\n')) | |
|
2910 | peer = httppeer.httpv2peer(ui, path, opener) | |
|
2911 | elif opts['peer'] == 'raw': | |
|
2905 | 2912 | ui.write(_('using raw connection to peer\n')) |
|
2906 | 2913 | peer = None |
|
2907 | 2914 | elif opts['peer']: |
@@ -2951,7 +2958,12 b' def debugwireproto(ui, repo, path=None, ' | |||
|
2951 | 2958 | else: |
|
2952 | 2959 | key, value = fields |
|
2953 | 2960 | |
|
2954 | args[key] = stringutil.unescapestr(value) | |
|
2961 | if value.startswith('eval:'): | |
|
2962 | value = stringutil.evalpythonliteral(value[5:]) | |
|
2963 | else: | |
|
2964 | value = stringutil.unescapestr(value) | |
|
2965 | ||
|
2966 | args[key] = value | |
|
2955 | 2967 | |
|
2956 | 2968 | if batchedcommands is not None: |
|
2957 | 2969 | batchedcommands.append((command, args)) |
@@ -16,6 +16,9 b' import struct' | |||
|
16 | 16 | import tempfile |
|
17 | 17 | |
|
18 | 18 | from .i18n import _ |
|
19 | from .thirdparty import ( | |
|
20 | cbor, | |
|
21 | ) | |
|
19 | 22 | from . import ( |
|
20 | 23 | bundle2, |
|
21 | 24 | error, |
@@ -25,6 +28,8 b' from . import (' | |||
|
25 | 28 | url as urlmod, |
|
26 | 29 | util, |
|
27 | 30 | wireproto, |
|
31 | wireprotoframing, | |
|
32 | wireprotoserver, | |
|
28 | 33 | ) |
|
29 | 34 | |
|
30 | 35 | httplib = util.httplib |
@@ -467,6 +472,95 b' class httppeer(wireproto.wirepeer):' | |||
|
467 | 472 | def _abort(self, exception): |
|
468 | 473 | raise exception |
|
469 | 474 | |
|
475 | # TODO implement interface for version 2 peers | |
|
476 | class httpv2peer(object): | |
|
477 | def __init__(self, ui, repourl, opener): | |
|
478 | self.ui = ui | |
|
479 | ||
|
480 | if repourl.endswith('/'): | |
|
481 | repourl = repourl[:-1] | |
|
482 | ||
|
483 | self.url = repourl | |
|
484 | self._opener = opener | |
|
485 | # This is an its own attribute to facilitate extensions overriding | |
|
486 | # the default type. | |
|
487 | self._requestbuilder = urlreq.request | |
|
488 | ||
|
489 | def close(self): | |
|
490 | pass | |
|
491 | ||
|
492 | # TODO require to be part of a batched primitive, use futures. | |
|
493 | def _call(self, name, **args): | |
|
494 | """Call a wire protocol command with arguments.""" | |
|
495 | ||
|
496 | # TODO permissions should come from capabilities results. | |
|
497 | permission = wireproto.commandsv2[name].permission | |
|
498 | if permission not in ('push', 'pull'): | |
|
499 | raise error.ProgrammingError('unknown permission type: %s' % | |
|
500 | permission) | |
|
501 | ||
|
502 | permission = { | |
|
503 | 'push': 'rw', | |
|
504 | 'pull': 'ro', | |
|
505 | }[permission] | |
|
506 | ||
|
507 | url = '%s/api/%s/%s/%s' % (self.url, wireprotoserver.HTTPV2, permission, | |
|
508 | name) | |
|
509 | ||
|
510 | # TODO modify user-agent to reflect v2. | |
|
511 | headers = { | |
|
512 | r'Accept': wireprotoserver.FRAMINGTYPE, | |
|
513 | r'Content-Type': wireprotoserver.FRAMINGTYPE, | |
|
514 | } | |
|
515 | ||
|
516 | # TODO this should be part of a generic peer for the frame-based | |
|
517 | # protocol. | |
|
518 | stream = wireprotoframing.stream(1) | |
|
519 | frames = wireprotoframing.createcommandframes(stream, 1, | |
|
520 | name, args) | |
|
521 | ||
|
522 | body = b''.join(map(bytes, frames)) | |
|
523 | req = self._requestbuilder(pycompat.strurl(url), body, headers) | |
|
524 | req.add_unredirected_header(r'Content-Length', r'%d' % len(body)) | |
|
525 | ||
|
526 | # TODO unify this code with httppeer. | |
|
527 | try: | |
|
528 | res = self._opener.open(req) | |
|
529 | except urlerr.httperror as e: | |
|
530 | if e.code == 401: | |
|
531 | raise error.Abort(_('authorization failed')) | |
|
532 | ||
|
533 | raise | |
|
534 | except httplib.HTTPException as e: | |
|
535 | self.ui.traceback() | |
|
536 | raise IOError(None, e) | |
|
537 | ||
|
538 | # TODO validate response type, wrap response to handle I/O errors. | |
|
539 | # TODO more robust frame receiver. | |
|
540 | results = [] | |
|
541 | ||
|
542 | while True: | |
|
543 | frame = wireprotoframing.readframe(res) | |
|
544 | if frame is None: | |
|
545 | break | |
|
546 | ||
|
547 | self.ui.note(_('received %r\n') % frame) | |
|
548 | ||
|
549 | if frame.typeid == wireprotoframing.FRAME_TYPE_BYTES_RESPONSE: | |
|
550 | if frame.flags & wireprotoframing.FLAG_BYTES_RESPONSE_CBOR: | |
|
551 | payload = util.bytesio(frame.payload) | |
|
552 | ||
|
553 | decoder = cbor.CBORDecoder(payload) | |
|
554 | while payload.tell() + 1 < len(frame.payload): | |
|
555 | results.append(decoder.decode()) | |
|
556 | else: | |
|
557 | results.append(frame.payload) | |
|
558 | else: | |
|
559 | error.ProgrammingError('unhandled frame type: %d' % | |
|
560 | frame.typeid) | |
|
561 | ||
|
562 | return results | |
|
563 | ||
|
470 | 564 | def makepeer(ui, path): |
|
471 | 565 | u = util.url(path) |
|
472 | 566 | if u.query or u.fragment: |
@@ -180,6 +180,36 b' Request to read-only command works out o' | |||
|
180 | 180 | s> 0\r\n |
|
181 | 181 | s> \r\n |
|
182 | 182 | |
|
183 | $ sendhttpv2peer << EOF | |
|
184 | > command customreadonly | |
|
185 | > EOF | |
|
186 | creating http peer for wire protocol version 2 | |
|
187 | sending customreadonly command | |
|
188 | s> POST /api/exp-http-v2-0001/ro/customreadonly HTTP/1.1\r\n | |
|
189 | s> Accept-Encoding: identity\r\n | |
|
190 | s> accept: application/mercurial-exp-framing-0003\r\n | |
|
191 | s> content-type: application/mercurial-exp-framing-0003\r\n | |
|
192 | s> content-length: 29\r\n | |
|
193 | s> host: $LOCALIP:$HGPORT\r\n (glob) | |
|
194 | s> user-agent: Mercurial debugwireproto\r\n | |
|
195 | s> \r\n | |
|
196 | s> \x15\x00\x00\x01\x00\x01\x01\x11\xa1DnameNcustomreadonly | |
|
197 | s> makefile('rb', None) | |
|
198 | s> HTTP/1.1 200 OK\r\n | |
|
199 | s> Server: testing stub value\r\n | |
|
200 | s> Date: $HTTP_DATE$\r\n | |
|
201 | s> Content-Type: application/mercurial-exp-framing-0003\r\n | |
|
202 | s> Transfer-Encoding: chunked\r\n | |
|
203 | s> \r\n | |
|
204 | s> 25\r\n | |
|
205 | s> \x1d\x00\x00\x01\x00\x02\x01B | |
|
206 | s> customreadonly bytes response | |
|
207 | s> \r\n | |
|
208 | received frame(size=29; request=1; stream=2; streamflags=stream-begin; type=bytes-response; flags=eos) | |
|
209 | s> 0\r\n | |
|
210 | s> \r\n | |
|
211 | response: [b'customreadonly bytes response'] | |
|
212 | ||
|
183 | 213 | Request to read-write command fails because server is read-only by default |
|
184 | 214 | |
|
185 | 215 | GET to read-write request yields 405 |
@@ -179,7 +179,7 b' Test listkeys for listing namespaces' | |||
|
179 | 179 | s> Accept-Encoding: identity\r\n |
|
180 | 180 | s> accept: application/mercurial-0.1\r\n |
|
181 | 181 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
182 |
s> user-agent: |
|
|
182 | s> user-agent: Mercurial debugwireproto\r\n | |
|
183 | 183 | s> \r\n |
|
184 | 184 | s> makefile('rb', None) |
|
185 | 185 | s> HTTP/1.1 200 Script output follows\r\n |
@@ -197,7 +197,7 b' Test listkeys for listing namespaces' | |||
|
197 | 197 | s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n |
|
198 | 198 | s> accept: application/mercurial-0.1\r\n |
|
199 | 199 | s> host: $LOCALIP:$HGPORT\r\n (glob) |
|
200 |
s> user-agent: |
|
|
200 | s> user-agent: Mercurial debugwireproto\r\n | |
|
201 | 201 | s> \r\n |
|
202 | 202 | s> makefile('rb', None) |
|
203 | 203 | s> HTTP/1.1 200 Script output follows\r\n |
General Comments 0
You need to be logged in to leave comments.
Login now