Mercurial > hg
comparison mercurial/wireprotoserver.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 7e19b640c53e |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
19 util, | 19 util, |
20 wireprototypes, | 20 wireprototypes, |
21 wireprotov1server, | 21 wireprotov1server, |
22 wireprotov2server, | 22 wireprotov2server, |
23 ) | 23 ) |
24 from .interfaces import ( | 24 from .interfaces import util as interfaceutil |
25 util as interfaceutil, | |
26 ) | |
27 from .utils import ( | 25 from .utils import ( |
28 cborutil, | 26 cborutil, |
29 compression, | 27 compression, |
30 ) | 28 ) |
31 | 29 |
40 HGTYPE2 = 'application/mercurial-0.2' | 38 HGTYPE2 = 'application/mercurial-0.2' |
41 HGERRTYPE = 'application/hg-error' | 39 HGERRTYPE = 'application/hg-error' |
42 | 40 |
43 SSHV1 = wireprototypes.SSHV1 | 41 SSHV1 = wireprototypes.SSHV1 |
44 SSHV2 = wireprototypes.SSHV2 | 42 SSHV2 = wireprototypes.SSHV2 |
43 | |
45 | 44 |
46 def decodevaluefromheaders(req, headerprefix): | 45 def decodevaluefromheaders(req, headerprefix): |
47 """Decode a long value from multiple HTTP request headers. | 46 """Decode a long value from multiple HTTP request headers. |
48 | 47 |
49 Returns the value as a bytes, not a str. | 48 Returns the value as a bytes, not a str. |
56 break | 55 break |
57 chunks.append(pycompat.bytesurl(v)) | 56 chunks.append(pycompat.bytesurl(v)) |
58 i += 1 | 57 i += 1 |
59 | 58 |
60 return ''.join(chunks) | 59 return ''.join(chunks) |
60 | |
61 | 61 |
62 @interfaceutil.implementer(wireprototypes.baseprotocolhandler) | 62 @interfaceutil.implementer(wireprototypes.baseprotocolhandler) |
63 class httpv1protocolhandler(object): | 63 class httpv1protocolhandler(object): |
64 def __init__(self, req, ui, checkperm): | 64 def __init__(self, req, ui, checkperm): |
65 self._req = req | 65 self._req = req |
88 | 88 |
89 def _args(self): | 89 def _args(self): |
90 args = self._req.qsparams.asdictoflists() | 90 args = self._req.qsparams.asdictoflists() |
91 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0)) | 91 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0)) |
92 if postlen: | 92 if postlen: |
93 args.update(urlreq.parseqs( | 93 args.update( |
94 self._req.bodyfh.read(postlen), keep_blank_values=True)) | 94 urlreq.parseqs( |
95 self._req.bodyfh.read(postlen), keep_blank_values=True | |
96 ) | |
97 ) | |
95 return args | 98 return args |
96 | 99 |
97 argvalue = decodevaluefromheaders(self._req, b'X-HgArg') | 100 argvalue = decodevaluefromheaders(self._req, b'X-HgArg') |
98 args.update(urlreq.parseqs(argvalue, keep_blank_values=True)) | 101 args.update(urlreq.parseqs(argvalue, keep_blank_values=True)) |
99 return args | 102 return args |
130 | 133 |
131 def client(self): | 134 def client(self): |
132 return 'remote:%s:%s:%s' % ( | 135 return 'remote:%s:%s:%s' % ( |
133 self._req.urlscheme, | 136 self._req.urlscheme, |
134 urlreq.quote(self._req.remotehost or ''), | 137 urlreq.quote(self._req.remotehost or ''), |
135 urlreq.quote(self._req.remoteuser or '')) | 138 urlreq.quote(self._req.remoteuser or ''), |
139 ) | |
136 | 140 |
137 def addcapabilities(self, repo, caps): | 141 def addcapabilities(self, repo, caps): |
138 caps.append(b'batch') | 142 caps.append(b'batch') |
139 | 143 |
140 caps.append('httpheader=%d' % | 144 caps.append( |
141 repo.ui.configint('server', 'maxhttpheaderlen')) | 145 'httpheader=%d' % repo.ui.configint('server', 'maxhttpheaderlen') |
146 ) | |
142 if repo.ui.configbool('experimental', 'httppostargs'): | 147 if repo.ui.configbool('experimental', 'httppostargs'): |
143 caps.append('httppostargs') | 148 caps.append('httppostargs') |
144 | 149 |
145 # FUTURE advertise 0.2rx once support is implemented | 150 # FUTURE advertise 0.2rx once support is implemented |
146 # FUTURE advertise minrx and mintx after consulting config option | 151 # FUTURE advertise minrx and mintx after consulting config option |
147 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx') | 152 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx') |
148 | 153 |
149 compengines = wireprototypes.supportedcompengines(repo.ui, | 154 compengines = wireprototypes.supportedcompengines( |
150 compression.SERVERROLE) | 155 repo.ui, compression.SERVERROLE |
156 ) | |
151 if compengines: | 157 if compengines: |
152 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name) | 158 comptypes = ','.join( |
153 for e in compengines) | 159 urlreq.quote(e.wireprotosupport().name) for e in compengines |
160 ) | |
154 caps.append('compression=%s' % comptypes) | 161 caps.append('compression=%s' % comptypes) |
155 | 162 |
156 return caps | 163 return caps |
157 | 164 |
158 def checkperm(self, perm): | 165 def checkperm(self, perm): |
159 return self._checkperm(perm) | 166 return self._checkperm(perm) |
167 | |
160 | 168 |
161 # This method exists mostly so that extensions like remotefilelog can | 169 # This method exists mostly so that extensions like remotefilelog can |
162 # disable a kludgey legacy method only over http. As of early 2018, | 170 # disable a kludgey legacy method only over http. As of early 2018, |
163 # there are no other known users, so with any luck we can discard this | 171 # there are no other known users, so with any luck we can discard this |
164 # hook if remotefilelog becomes a first-party extension. | 172 # hook if remotefilelog becomes a first-party extension. |
165 def iscmd(cmd): | 173 def iscmd(cmd): |
166 return cmd in wireprotov1server.commands | 174 return cmd in wireprotov1server.commands |
175 | |
167 | 176 |
168 def handlewsgirequest(rctx, req, res, checkperm): | 177 def handlewsgirequest(rctx, req, res, checkperm): |
169 """Possibly process a wire protocol request. | 178 """Possibly process a wire protocol request. |
170 | 179 |
171 If the current request is a wire protocol request, the request is | 180 If the current request is a wire protocol request, the request is |
210 # TODO This is not a good response to issue for this request. This | 219 # TODO This is not a good response to issue for this request. This |
211 # is mostly for BC for now. | 220 # is mostly for BC for now. |
212 res.setbodybytes('0\n%s\n' % b'Not Found') | 221 res.setbodybytes('0\n%s\n' % b'Not Found') |
213 return True | 222 return True |
214 | 223 |
215 proto = httpv1protocolhandler(req, repo.ui, | 224 proto = httpv1protocolhandler( |
216 lambda perm: checkperm(rctx, req, perm)) | 225 req, repo.ui, lambda perm: checkperm(rctx, req, perm) |
226 ) | |
217 | 227 |
218 # The permissions checker should be the only thing that can raise an | 228 # The permissions checker should be the only thing that can raise an |
219 # ErrorResponse. It is kind of a layer violation to catch an hgweb | 229 # ErrorResponse. It is kind of a layer violation to catch an hgweb |
220 # exception here. So consider refactoring into a exception type that | 230 # exception here. So consider refactoring into a exception type that |
221 # is associated with the wire protocol. | 231 # is associated with the wire protocol. |
229 # "unbundle." That assumption is not always valid. | 239 # "unbundle." That assumption is not always valid. |
230 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e)) | 240 res.setbodybytes('0\n%s\n' % pycompat.bytestr(e)) |
231 | 241 |
232 return True | 242 return True |
233 | 243 |
244 | |
234 def _availableapis(repo): | 245 def _availableapis(repo): |
235 apis = set() | 246 apis = set() |
236 | 247 |
237 # Registered APIs are made available via config options of the name of | 248 # Registered APIs are made available via config options of the name of |
238 # the protocol. | 249 # the protocol. |
240 section, option = v['config'] | 251 section, option = v['config'] |
241 if repo.ui.configbool(section, option): | 252 if repo.ui.configbool(section, option): |
242 apis.add(k) | 253 apis.add(k) |
243 | 254 |
244 return apis | 255 return apis |
256 | |
245 | 257 |
246 def handlewsgiapirequest(rctx, req, res, checkperm): | 258 def handlewsgiapirequest(rctx, req, res, checkperm): |
247 """Handle requests to /api/*.""" | 259 """Handle requests to /api/*.""" |
248 assert req.dispatchparts[0] == b'api' | 260 assert req.dispatchparts[0] == b'api' |
249 | 261 |
264 | 276 |
265 # Requests to /api/ list available APIs. | 277 # Requests to /api/ list available APIs. |
266 if req.dispatchparts == [b'api']: | 278 if req.dispatchparts == [b'api']: |
267 res.status = b'200 OK' | 279 res.status = b'200 OK' |
268 res.headers[b'Content-Type'] = b'text/plain' | 280 res.headers[b'Content-Type'] = b'text/plain' |
269 lines = [_('APIs can be accessed at /api/<name>, where <name> can be ' | 281 lines = [ |
270 'one of the following:\n')] | 282 _( |
283 'APIs can be accessed at /api/<name>, where <name> can be ' | |
284 'one of the following:\n' | |
285 ) | |
286 ] | |
271 if availableapis: | 287 if availableapis: |
272 lines.extend(sorted(availableapis)) | 288 lines.extend(sorted(availableapis)) |
273 else: | 289 else: |
274 lines.append(_('(no available APIs)\n')) | 290 lines.append(_('(no available APIs)\n')) |
275 res.setbodybytes(b'\n'.join(lines)) | 291 res.setbodybytes(b'\n'.join(lines)) |
278 proto = req.dispatchparts[1] | 294 proto = req.dispatchparts[1] |
279 | 295 |
280 if proto not in API_HANDLERS: | 296 if proto not in API_HANDLERS: |
281 res.status = b'404 Not Found' | 297 res.status = b'404 Not Found' |
282 res.headers[b'Content-Type'] = b'text/plain' | 298 res.headers[b'Content-Type'] = b'text/plain' |
283 res.setbodybytes(_('Unknown API: %s\nKnown APIs: %s') % ( | 299 res.setbodybytes( |
284 proto, b', '.join(sorted(availableapis)))) | 300 _('Unknown API: %s\nKnown APIs: %s') |
301 % (proto, b', '.join(sorted(availableapis))) | |
302 ) | |
285 return | 303 return |
286 | 304 |
287 if proto not in availableapis: | 305 if proto not in availableapis: |
288 res.status = b'404 Not Found' | 306 res.status = b'404 Not Found' |
289 res.headers[b'Content-Type'] = b'text/plain' | 307 res.headers[b'Content-Type'] = b'text/plain' |
290 res.setbodybytes(_('API %s not enabled\n') % proto) | 308 res.setbodybytes(_('API %s not enabled\n') % proto) |
291 return | 309 return |
292 | 310 |
293 API_HANDLERS[proto]['handler'](rctx, req, res, checkperm, | 311 API_HANDLERS[proto]['handler']( |
294 req.dispatchparts[2:]) | 312 rctx, req, res, checkperm, req.dispatchparts[2:] |
313 ) | |
314 | |
295 | 315 |
296 # Maps API name to metadata so custom API can be registered. | 316 # Maps API name to metadata so custom API can be registered. |
297 # Keys are: | 317 # Keys are: |
298 # | 318 # |
299 # config | 319 # config |
310 'handler': wireprotov2server.handlehttpv2request, | 330 'handler': wireprotov2server.handlehttpv2request, |
311 'apidescriptor': wireprotov2server.httpv2apidescriptor, | 331 'apidescriptor': wireprotov2server.httpv2apidescriptor, |
312 }, | 332 }, |
313 } | 333 } |
314 | 334 |
335 | |
315 def _httpresponsetype(ui, proto, prefer_uncompressed): | 336 def _httpresponsetype(ui, proto, prefer_uncompressed): |
316 """Determine the appropriate response type and compression settings. | 337 """Determine the appropriate response type and compression settings. |
317 | 338 |
318 Returns a tuple of (mediatype, compengine, engineopts). | 339 Returns a tuple of (mediatype, compengine, engineopts). |
319 """ | 340 """ |
325 if prefer_uncompressed: | 346 if prefer_uncompressed: |
326 return HGTYPE2, compression._noopengine(), {} | 347 return HGTYPE2, compression._noopengine(), {} |
327 | 348 |
328 # Now find an agreed upon compression format. | 349 # Now find an agreed upon compression format. |
329 compformats = wireprotov1server.clientcompressionsupport(proto) | 350 compformats = wireprotov1server.clientcompressionsupport(proto) |
330 for engine in wireprototypes.supportedcompengines(ui, | 351 for engine in wireprototypes.supportedcompengines( |
331 compression.SERVERROLE): | 352 ui, compression.SERVERROLE |
353 ): | |
332 if engine.wireprotosupport().name in compformats: | 354 if engine.wireprotosupport().name in compformats: |
333 opts = {} | 355 opts = {} |
334 level = ui.configint('server', '%slevel' % engine.name()) | 356 level = ui.configint('server', '%slevel' % engine.name()) |
335 if level is not None: | 357 if level is not None: |
336 opts['level'] = level | 358 opts['level'] = level |
343 # Don't allow untrusted settings because disabling compression or | 365 # Don't allow untrusted settings because disabling compression or |
344 # setting a very high compression level could lead to flooding | 366 # setting a very high compression level could lead to flooding |
345 # the server's network or CPU. | 367 # the server's network or CPU. |
346 opts = {'level': ui.configint('server', 'zliblevel')} | 368 opts = {'level': ui.configint('server', 'zliblevel')} |
347 return HGTYPE, util.compengines['zlib'], opts | 369 return HGTYPE, util.compengines['zlib'], opts |
370 | |
348 | 371 |
349 def processcapabilitieshandshake(repo, req, res, proto): | 372 def processcapabilitieshandshake(repo, req, res, proto): |
350 """Called during a ?cmd=capabilities request. | 373 """Called during a ?cmd=capabilities request. |
351 | 374 |
352 If the client is advertising support for a newer protocol, we send | 375 If the client is advertising support for a newer protocol, we send |
392 res.headers[b'Content-Type'] = b'application/mercurial-cbor' | 415 res.headers[b'Content-Type'] = b'application/mercurial-cbor' |
393 res.setbodybytes(b''.join(cborutil.streamencode(m))) | 416 res.setbodybytes(b''.join(cborutil.streamencode(m))) |
394 | 417 |
395 return True | 418 return True |
396 | 419 |
420 | |
397 def _callhttp(repo, req, res, proto, cmd): | 421 def _callhttp(repo, req, res, proto, cmd): |
398 # Avoid cycle involving hg module. | 422 # Avoid cycle involving hg module. |
399 from .hgweb import common as hgwebcommon | 423 from .hgweb import common as hgwebcommon |
400 | 424 |
401 def genversion2(gen, engine, engineopts): | 425 def genversion2(gen, engine, engineopts): |
421 res.setbodybytes(bodybytes) | 445 res.setbodybytes(bodybytes) |
422 if bodygen is not None: | 446 if bodygen is not None: |
423 res.setbodygen(bodygen) | 447 res.setbodygen(bodygen) |
424 | 448 |
425 if not wireprotov1server.commands.commandavailable(cmd, proto): | 449 if not wireprotov1server.commands.commandavailable(cmd, proto): |
426 setresponse(HTTP_OK, HGERRTYPE, | 450 setresponse( |
427 _('requested wire protocol command is not available over ' | 451 HTTP_OK, |
428 'HTTP')) | 452 HGERRTYPE, |
453 _('requested wire protocol command is not available over ' 'HTTP'), | |
454 ) | |
429 return | 455 return |
430 | 456 |
431 proto.checkperm(wireprotov1server.commands[cmd].permission) | 457 proto.checkperm(wireprotov1server.commands[cmd].permission) |
432 | 458 |
433 # Possibly handle a modern client wanting to switch protocols. | 459 # Possibly handle a modern client wanting to switch protocols. |
434 if (cmd == 'capabilities' and | 460 if cmd == 'capabilities' and processcapabilitieshandshake( |
435 processcapabilitieshandshake(repo, req, res, proto)): | 461 repo, req, res, proto |
462 ): | |
436 | 463 |
437 return | 464 return |
438 | 465 |
439 rsp = wireprotov1server.dispatch(repo, proto, cmd) | 466 rsp = wireprotov1server.dispatch(repo, proto, cmd) |
440 | 467 |
448 gen = rsp.gen | 475 gen = rsp.gen |
449 | 476 |
450 # This code for compression should not be streamres specific. It | 477 # This code for compression should not be streamres specific. It |
451 # is here because we only compress streamres at the moment. | 478 # is here because we only compress streamres at the moment. |
452 mediatype, engine, engineopts = _httpresponsetype( | 479 mediatype, engine, engineopts = _httpresponsetype( |
453 repo.ui, proto, rsp.prefer_uncompressed) | 480 repo.ui, proto, rsp.prefer_uncompressed |
481 ) | |
454 gen = engine.compressstream(gen, engineopts) | 482 gen = engine.compressstream(gen, engineopts) |
455 | 483 |
456 if mediatype == HGTYPE2: | 484 if mediatype == HGTYPE2: |
457 gen = genversion2(gen, engine, engineopts) | 485 gen = genversion2(gen, engine, engineopts) |
458 | 486 |
467 elif isinstance(rsp, wireprototypes.ooberror): | 495 elif isinstance(rsp, wireprototypes.ooberror): |
468 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message) | 496 setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message) |
469 else: | 497 else: |
470 raise error.ProgrammingError('hgweb.protocol internal failure', rsp) | 498 raise error.ProgrammingError('hgweb.protocol internal failure', rsp) |
471 | 499 |
500 | |
472 def _sshv1respondbytes(fout, value): | 501 def _sshv1respondbytes(fout, value): |
473 """Send a bytes response for protocol version 1.""" | 502 """Send a bytes response for protocol version 1.""" |
474 fout.write('%d\n' % len(value)) | 503 fout.write('%d\n' % len(value)) |
475 fout.write(value) | 504 fout.write(value) |
476 fout.flush() | 505 fout.flush() |
477 | 506 |
507 | |
478 def _sshv1respondstream(fout, source): | 508 def _sshv1respondstream(fout, source): |
479 write = fout.write | 509 write = fout.write |
480 for chunk in source.gen: | 510 for chunk in source.gen: |
481 write(chunk) | 511 write(chunk) |
482 fout.flush() | 512 fout.flush() |
483 | 513 |
514 | |
484 def _sshv1respondooberror(fout, ferr, rsp): | 515 def _sshv1respondooberror(fout, ferr, rsp): |
485 ferr.write(b'%s\n-\n' % rsp) | 516 ferr.write(b'%s\n-\n' % rsp) |
486 ferr.flush() | 517 ferr.flush() |
487 fout.write(b'\n') | 518 fout.write(b'\n') |
488 fout.flush() | 519 fout.flush() |
489 | 520 |
521 | |
490 @interfaceutil.implementer(wireprototypes.baseprotocolhandler) | 522 @interfaceutil.implementer(wireprototypes.baseprotocolhandler) |
491 class sshv1protocolhandler(object): | 523 class sshv1protocolhandler(object): |
492 """Handler for requests services via version 1 of SSH protocol.""" | 524 """Handler for requests services via version 1 of SSH protocol.""" |
525 | |
493 def __init__(self, ui, fin, fout): | 526 def __init__(self, ui, fin, fout): |
494 self._ui = ui | 527 self._ui = ui |
495 self._fin = fin | 528 self._fin = fin |
496 self._fout = fout | 529 self._fout = fout |
497 self._protocaps = set() | 530 self._protocaps = set() |
555 return caps | 588 return caps |
556 | 589 |
557 def checkperm(self, perm): | 590 def checkperm(self, perm): |
558 pass | 591 pass |
559 | 592 |
593 | |
560 class sshv2protocolhandler(sshv1protocolhandler): | 594 class sshv2protocolhandler(sshv1protocolhandler): |
561 """Protocol handler for version 2 of the SSH protocol.""" | 595 """Protocol handler for version 2 of the SSH protocol.""" |
562 | 596 |
563 @property | 597 @property |
564 def name(self): | 598 def name(self): |
565 return wireprototypes.SSHV2 | 599 return wireprototypes.SSHV2 |
566 | 600 |
567 def addcapabilities(self, repo, caps): | 601 def addcapabilities(self, repo, caps): |
568 return caps | 602 return caps |
603 | |
569 | 604 |
570 def _runsshserver(ui, repo, fin, fout, ev): | 605 def _runsshserver(ui, repo, fin, fout, ev): |
571 # This function operates like a state machine of sorts. The following | 606 # This function operates like a state machine of sorts. The following |
572 # states are defined: | 607 # states are defined: |
573 # | 608 # |
636 | 671 |
637 # It looks like a protocol upgrade request. Transition state to | 672 # It looks like a protocol upgrade request. Transition state to |
638 # handle it. | 673 # handle it. |
639 if request.startswith(b'upgrade '): | 674 if request.startswith(b'upgrade '): |
640 if protoswitched: | 675 if protoswitched: |
641 _sshv1respondooberror(fout, ui.ferr, | 676 _sshv1respondooberror( |
642 b'cannot upgrade protocols multiple ' | 677 fout, |
643 b'times') | 678 ui.ferr, |
679 b'cannot upgrade protocols multiple ' b'times', | |
680 ) | |
644 state = 'shutdown' | 681 state = 'shutdown' |
645 continue | 682 continue |
646 | 683 |
647 state = 'upgrade-initial' | 684 state = 'upgrade-initial' |
648 continue | 685 continue |
649 | 686 |
650 available = wireprotov1server.commands.commandavailable( | 687 available = wireprotov1server.commands.commandavailable( |
651 request, proto) | 688 request, proto |
689 ) | |
652 | 690 |
653 # This command isn't available. Send an empty response and go | 691 # This command isn't available. Send an empty response and go |
654 # back to waiting for a new command. | 692 # back to waiting for a new command. |
655 if not available: | 693 if not available: |
656 _sshv1respondbytes(fout, b'') | 694 _sshv1respondbytes(fout, b'') |
674 elif isinstance(rsp, wireprototypes.pusherr): | 712 elif isinstance(rsp, wireprototypes.pusherr): |
675 _sshv1respondbytes(fout, rsp.res) | 713 _sshv1respondbytes(fout, rsp.res) |
676 elif isinstance(rsp, wireprototypes.ooberror): | 714 elif isinstance(rsp, wireprototypes.ooberror): |
677 _sshv1respondooberror(fout, ui.ferr, rsp.message) | 715 _sshv1respondooberror(fout, ui.ferr, rsp.message) |
678 else: | 716 else: |
679 raise error.ProgrammingError('unhandled response type from ' | 717 raise error.ProgrammingError( |
680 'wire protocol command: %s' % rsp) | 718 'unhandled response type from ' |
719 'wire protocol command: %s' % rsp | |
720 ) | |
681 | 721 |
682 # For now, protocol version 2 serving just goes back to version 1. | 722 # For now, protocol version 2 serving just goes back to version 1. |
683 elif state == 'protov2-serving': | 723 elif state == 'protov2-serving': |
684 state = 'protov1-serving' | 724 state = 'protov1-serving' |
685 continue | 725 continue |
739 ok = True | 779 ok = True |
740 for line in (b'hello', b'between', b'pairs 81'): | 780 for line in (b'hello', b'between', b'pairs 81'): |
741 request = fin.readline()[:-1] | 781 request = fin.readline()[:-1] |
742 | 782 |
743 if request != line: | 783 if request != line: |
744 _sshv1respondooberror(fout, ui.ferr, | 784 _sshv1respondooberror( |
745 b'malformed handshake protocol: ' | 785 fout, |
746 b'missing %s' % line) | 786 ui.ferr, |
787 b'malformed handshake protocol: ' b'missing %s' % line, | |
788 ) | |
747 ok = False | 789 ok = False |
748 state = 'shutdown' | 790 state = 'shutdown' |
749 break | 791 break |
750 | 792 |
751 if not ok: | 793 if not ok: |
752 continue | 794 continue |
753 | 795 |
754 request = fin.read(81) | 796 request = fin.read(81) |
755 if request != b'%s-%s' % (b'0' * 40, b'0' * 40): | 797 if request != b'%s-%s' % (b'0' * 40, b'0' * 40): |
756 _sshv1respondooberror(fout, ui.ferr, | 798 _sshv1respondooberror( |
757 b'malformed handshake protocol: ' | 799 fout, |
758 b'missing between argument value') | 800 ui.ferr, |
801 b'malformed handshake protocol: ' | |
802 b'missing between argument value', | |
803 ) | |
759 state = 'shutdown' | 804 state = 'shutdown' |
760 continue | 805 continue |
761 | 806 |
762 state = 'upgrade-v2-finish' | 807 state = 'upgrade-v2-finish' |
763 continue | 808 continue |
778 | 823 |
779 elif state == 'shutdown': | 824 elif state == 'shutdown': |
780 break | 825 break |
781 | 826 |
782 else: | 827 else: |
783 raise error.ProgrammingError('unhandled ssh server state: %s' % | 828 raise error.ProgrammingError( |
784 state) | 829 'unhandled ssh server state: %s' % state |
830 ) | |
831 | |
785 | 832 |
786 class sshserver(object): | 833 class sshserver(object): |
787 def __init__(self, ui, repo, logfh=None): | 834 def __init__(self, ui, repo, logfh=None): |
788 self._ui = ui | 835 self._ui = ui |
789 self._repo = repo | 836 self._repo = repo |
790 self._fin, self._fout = ui.protectfinout() | 837 self._fin, self._fout = ui.protectfinout() |
791 | 838 |
792 # Log write I/O to stdout and stderr if configured. | 839 # Log write I/O to stdout and stderr if configured. |
793 if logfh: | 840 if logfh: |
794 self._fout = util.makeloggingfileobject( | 841 self._fout = util.makeloggingfileobject( |
795 logfh, self._fout, 'o', logdata=True) | 842 logfh, self._fout, 'o', logdata=True |
843 ) | |
796 ui.ferr = util.makeloggingfileobject( | 844 ui.ferr = util.makeloggingfileobject( |
797 logfh, ui.ferr, 'e', logdata=True) | 845 logfh, ui.ferr, 'e', logdata=True |
846 ) | |
798 | 847 |
799 def serve_forever(self): | 848 def serve_forever(self): |
800 self.serveuntil(threading.Event()) | 849 self.serveuntil(threading.Event()) |
801 self._ui.restorefinout(self._fin, self._fout) | 850 self._ui.restorefinout(self._fin, self._fout) |
802 sys.exit(0) | 851 sys.exit(0) |