comparison mercurial/sshpeer.py @ 35979:a622a927fe03

sshpeer: document the handshake mechanism The mechanism by which SSH peers establish connections with remotes is wonky and requires a bit of code archeology to understand. While it is already documented in `hg help internals.wireproto`, it helps to have documentation in the code as well. Differential Revision: https://phab.mercurial-scm.org/D2035
author Gregory Szorc <gregory.szorc@gmail.com>
date Sun, 04 Feb 2018 14:44:04 -0800
parents 80a2b8ae42a1
children 556218e08e25
comparison
equal deleted inserted replaced
35978:80a2b8ae42a1 35979:a622a927fe03
160 def badresponse(): 160 def badresponse():
161 msg = _('no suitable response from remote hg') 161 msg = _('no suitable response from remote hg')
162 hint = ui.config('ui', 'ssherrorhint') 162 hint = ui.config('ui', 'ssherrorhint')
163 raise error.RepoError(msg, hint=hint) 163 raise error.RepoError(msg, hint=hint)
164 164
165 # The handshake consists of sending 2 wire protocol commands:
166 # ``hello`` and ``between``.
167 #
168 # The ``hello`` command (which was introduced in Mercurial 0.9.1)
169 # instructs the server to advertise its capabilities.
170 #
171 # The ``between`` command (which has existed in all Mercurial servers
172 # for as long as SSH support has existed), asks for the set of revisions
173 # between a pair of revisions.
174 #
175 # The ``between`` command is issued with a request for the null
176 # range. If the remote is a Mercurial server, this request will
177 # generate a specific response: ``1\n\n``. This represents the
178 # wire protocol encoded value for ``\n``. We look for ``1\n\n``
179 # in the output stream and know this is the response to ``between``
180 # and we're at the end of our handshake reply.
181 #
182 # The response to the ``hello`` command will be a line with the
183 # length of the value returned by that command followed by that
184 # value. If the server doesn't support ``hello`` (which should be
185 # rare), that line will be ``0\n``. Otherwise, the value will contain
186 # RFC 822 like lines. Of these, the ``capabilities:`` line contains
187 # the capabilities of the server.
188 #
189 # In addition to the responses to our command requests, the server
190 # may emit "banner" output on stdout. SSH servers are allowed to
191 # print messages to stdout on login. Issuing commands on connection
192 # allows us to flush this banner output from the server by scanning
193 # for output to our well-known ``between`` command. Of course, if
194 # the banner contains ``1\n\n``, this will throw off our detection.
195
165 requestlog = ui.configbool('devel', 'debug.peer-request') 196 requestlog = ui.configbool('devel', 'debug.peer-request')
166 197
167 try: 198 try:
168 pairsarg = '%s-%s' % ('0' * 40, '0' * 40) 199 pairsarg = '%s-%s' % ('0' * 40, '0' * 40)
169 handshake = [ 200 handshake = [
203 else: 234 else:
204 badresponse() 235 badresponse()
205 236
206 caps = set() 237 caps = set()
207 for l in reversed(lines): 238 for l in reversed(lines):
239 # Look for response to ``hello`` command. Scan from the back so
240 # we don't misinterpret banner output as the command reply.
208 if l.startswith('capabilities:'): 241 if l.startswith('capabilities:'):
209 caps.update(l[:-1].split(':')[1].split()) 242 caps.update(l[:-1].split(':')[1].split())
210 break 243 break
211 244
212 return caps 245 return caps