Patchwork D2726: debugcommands: support connecting to HTTP peers

login
register
mail settings
Submitter phabricator
Date March 13, 2018, 12:08 a.m.
Message ID <aa87dbf2852d8ab802170bb9179fc123@localhost.localdomain>
Download mbox | patch
Permalink /patch/29422/
State Not Applicable
Headers show

Comments

phabricator - March 13, 2018, 12:08 a.m.
indygreg updated this revision to Diff 6977.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2726?vs=6721&id=6977

REVISION DETAIL
  https://phab.mercurial-scm.org/D2726

AFFECTED FILES
  mercurial/debugcommands.py
  tests/test-http-protocol.t

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: mercurial-devel

Patch

diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t
--- a/tests/test-http-protocol.t
+++ b/tests/test-http-protocol.t
@@ -161,3 +161,68 @@ 
   0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
   0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib|
   0020: 78                                              |x|
+
+  $ killdaemons.py
+  $ cd ..
+
+Test listkeys for listing namespaces
+
+  $ hg init empty
+  $ hg -R empty serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+  $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
+  > command listkeys
+  >     namespace namespaces
+  > EOF
+  s> sendall(*, 0): (glob)
+  s>     GET /?cmd=capabilities HTTP/1.1\r\n
+  s>     Accept-Encoding: identity\r\n
+  s>     accept: application/mercurial-0.1\r\n
+  s>     host: $LOCALIP:$HGPORT\r\n (glob)
+  s>     user-agent: mercurial/proto-1.0 (Mercurial *)\r\n (glob)
+  s>     \r\n
+  s> makefile('rb', None)
+  s> readline() -> 36:
+  s>     HTTP/1.1 200 Script output follows\r\n
+  s> readline() -> *: (glob)
+  s>     Server: *\r\n (glob)
+  s> readline() -> *: (glob)
+  s>     Date: *\r\n (glob)
+  s> readline() -> 41:
+  s>     Content-Type: application/mercurial-0.1\r\n
+  s> readline() -> 21:
+  s>     Content-Length: *\r\n (glob)
+  s> readline() -> 2:
+  s>     \r\n
+  s> read(*) -> *: lookup branchmap pushkey known getbundle unbundlehash batch changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx compression=$BUNDLE2_COMPRESSIONS$ (glob)
+  sending listkeys command
+  s> sendall(*, 0): (glob)
+  s>     GET /?cmd=listkeys HTTP/1.1\r\n
+  s>     Accept-Encoding: identity\r\n
+  s>     vary: X-HgArg-1,X-HgProto-1\r\n
+  s>     x-hgarg-1: namespace=namespaces\r\n
+  s>     x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
+  s>     accept: application/mercurial-0.1\r\n
+  s>     host: $LOCALIP:$HGPORT\r\n (glob)
+  s>     user-agent: mercurial/proto-1.0 (Mercurial *)\r\n (glob)
+  s>     \r\n
+  s> makefile('rb', None)
+  s> readline() -> 36:
+  s>     HTTP/1.1 200 Script output follows\r\n
+  s> readline() -> 36:
+  s>     Server: *\r\n (glob)
+  s> readline() -> *: (glob)
+  s>     Date: *\r\n (glob)
+  s> readline() -> 41:
+  s>     Content-Type: application/mercurial-0.1\r\n
+  s> readline() -> 20:
+  s>     Content-Length: 30\r\n
+  s> readline() -> 2:
+  s>     \r\n
+  s> read(30) -> 30:
+  s>     bookmarks	\n
+  s>     namespaces	\n
+  s>     phases	
+  response: bookmarks	\nnamespaces	\nphases	
+
+  $ killdaemons.py
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -48,6 +48,7 @@ 
     fileset,
     formatter,
     hg,
+    httppeer,
     localrepo,
     lock as lockmod,
     logcmdutil,
@@ -2588,9 +2589,9 @@ 
         ('', 'peer', '', _('construct a specific version of the peer')),
         ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
     ] + cmdutil.remoteopts,
-    _('[REPO]'),
+    _('[PATH]'),
     optionalrepo=True)
-def debugwireproto(ui, repo, **opts):
+def debugwireproto(ui, repo, path=None, **opts):
     """send wire protocol commands to a server
 
     This command can be used to issue wire protocol commands to remote
@@ -2726,12 +2727,19 @@ 
         raise error.Abort(_('invalid value for --peer'),
                           hint=_('valid values are "raw", "ssh1", and "ssh2"'))
 
+    if path and opts['localssh']:
+        raise error.Abort(_('cannot specify --localssh with an explicit '
+                            'path'))
+
     if ui.interactive():
         ui.write(_('(waiting for commands on stdin)\n'))
 
     blocks = list(_parsewirelangblocks(ui.fin))
 
     proc = None
+    stdin = None
+    stdout = None
+    stderr = None
 
     if opts['localssh']:
         # We start the SSH server in its own process so there is process
@@ -2779,22 +2787,55 @@ 
             peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
                                     autoreadstderr=autoreadstderr)
 
+    elif path:
+        # We bypass hg.peer() so we can proxy the sockets.
+        # TODO consider not doing this because we skip
+        # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
+        u = util.url(path)
+        if u.scheme != 'http':
+            raise error.Abort(_('only http:// paths are currently supported'))
+
+        if opts['peer']:
+            raise error.Abort(_('--peer is not supported with HTTP peers'))
+
+        url, authinfo = u.authinfo()
+        openerargs = {}
+
+        # Turn pipes/sockets into observers so we can log I/O.
+        if ui.verbose:
+            openerargs = {
+                r'loggingfh': ui,
+                r'loggingname': b's',
+                r'loggingopts': {
+                    r'logdata': True,
+                },
+            }
+
+        opener = urlmod.opener(ui, authinfo, **openerargs)
+        peer = httppeer.httppeer(ui, path, url, opener)
+        peer._fetchcaps()
+        # We /could/ populate stdin/stdout with sock.makefile()...
     else:
-        raise error.Abort(_('only --localssh is currently supported'))
+        raise error.Abort(_('unsupported connection configuration'))
 
     batchedcommands = None
 
     # Now perform actions based on the parsed wire language instructions.
     for action, lines in blocks:
         if action in ('raw', 'raw+'):
+            if not stdin:
+                raise error.Abort(_('cannot call raw/raw+ on this peer'))
+
             # Concatenate the data together.
             data = ''.join(l.lstrip() for l in lines)
             data = util.unescapestr(data)
             stdin.write(data)
 
             if action == 'raw+':
                 stdin.flush()
         elif action == 'flush':
+            if not stdin:
+                raise error.Abort(_('cannot call flush on this peer'))
             stdin.flush()
         elif action.startswith('command'):
             if not peer:
@@ -2851,18 +2892,30 @@ 
         elif action == 'close':
             peer.close()
         elif action == 'readavailable':
+            if not stdout or not stderr:
+                raise error.Abort(_('readavailable not available on this peer'))
+
             stdin.close()
             stdout.read()
             stderr.read()
+
         elif action == 'readline':
+            if not stdout:
+                raise error.Abort(_('readline not available on this peer'))
             stdout.readline()
         elif action == 'ereadline':
+            if not stderr:
+                raise error.Abort(_('ereadline not available on this peer'))
             stderr.readline()
         elif action.startswith('read '):
             count = int(action.split(' ', 1)[1])
+            if not stdout:
+                raise error.Abort(_('read not available on this peer'))
             stdout.read(count)
         elif action.startswith('eread '):
             count = int(action.split(' ', 1)[1])
+            if not stderr:
+                raise error.Abort(_('eread not available on this peer'))
             stderr.read(count)
         else:
             raise error.Abort(_('unknown action: %s') % action)