Mercurial > public > mercurial-scm > hg-stable
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 |