31
32
from bzrlib import (
36
38
from bzrlib.smart.protocol import (
39
MESSAGE_VERSION_THREE,
37
40
REQUEST_VERSION_TWO,
38
41
SmartClientRequestProtocolOne,
39
42
SmartServerRequestProtocolOne,
40
43
SmartServerRequestProtocolTwo,
44
build_server_protocol_three
42
46
from bzrlib.transport import ssh
49
def _get_protocol_factory_for_bytes(bytes):
50
"""Determine the right protocol factory for 'bytes'.
52
This will return an appropriate protocol factory depending on the version
53
of the protocol being used, as determined by inspecting the given bytes.
54
The bytes should have at least one newline byte (i.e. be a whole line),
55
otherwise it's possible that a request will be incorrectly identified as
58
Typical use would be::
60
factory, unused_bytes = _get_protocol_factory_for_bytes(bytes)
61
server_protocol = factory(transport, write_func, root_client_path)
62
server_protocol.accept_bytes(unused_bytes)
64
:param bytes: a str of bytes of the start of the request.
65
:returns: 2-tuple of (protocol_factory, unused_bytes). protocol_factory is
66
a callable that takes three args: transport, write_func,
67
root_client_path. unused_bytes are any bytes that were not part of a
68
protocol version marker.
70
if bytes.startswith(MESSAGE_VERSION_THREE):
71
protocol_factory = build_server_protocol_three
72
bytes = bytes[len(MESSAGE_VERSION_THREE):]
73
elif bytes.startswith(REQUEST_VERSION_TWO):
74
protocol_factory = SmartServerRequestProtocolTwo
75
bytes = bytes[len(REQUEST_VERSION_TWO):]
77
protocol_factory = SmartServerRequestProtocolOne
78
return protocol_factory, bytes
45
81
class SmartServerStreamMedium(object):
46
82
"""Handles smart commands coming over a stream.
117
153
:returns: a SmartServerRequestProtocol.
119
# Identify the protocol version.
120
155
bytes = self._get_line()
121
if bytes.startswith(REQUEST_VERSION_TWO):
122
protocol_class = SmartServerRequestProtocolTwo
123
bytes = bytes[len(REQUEST_VERSION_TWO):]
125
protocol_class = SmartServerRequestProtocolOne
126
protocol = protocol_class(
156
protocol_factory, unused_bytes = _get_protocol_factory_for_bytes(bytes)
157
protocol = protocol_factory(
127
158
self.backing_transport, self._write_out, self.root_client_path)
128
protocol.accept_bytes(bytes)
159
protocol.accept_bytes(unused_bytes)
131
162
def _serve_one_request(self, protocol):
195
226
protocol.accept_bytes(bytes)
197
self._push_back(protocol.excess_buffer)
228
self._push_back(protocol.unused_data)
199
230
def _get_bytes(self, desired_count):
200
231
if self._push_back_buffer is not None:
204
235
return self.socket.recv(4096)
206
237
def terminate_due_to_error(self):
207
"""Called when an unhandled exception from the protocol occurs."""
208
238
# TODO: This should log to a server log file, but no such thing
209
239
# exists yet. Andrew Bennetts 2006-09-29.
210
240
self.socket.close()
403
433
class SmartClientMedium(object):
404
434
"""Smart client is a medium for sending smart protocol requests over."""
436
def __init__(self, base):
407
437
super(SmartClientMedium, self).__init__()
408
439
self._protocol_version_error = None
409
440
self._protocol_version = None
441
self._done_hello = False
442
# Be optimistic: we assume the remote end can accept new remote
443
# requests until we get an error saying otherwise. (1.2 adds some
444
# requests that send bodies, which confuses older servers.)
445
self._remote_is_at_least_1_2 = True
411
447
def protocol_version(self):
412
"""Find out the best protocol version to use."""
448
"""Find out if 'hello' smart request works."""
413
449
if self._protocol_version_error is not None:
414
450
raise self._protocol_version_error
415
if self._protocol_version is None:
451
if not self._done_hello:
417
453
medium_request = self.get_request()
418
454
# Send a 'hello' request in protocol version one, for maximum
419
455
# backwards compatibility.
420
456
client_protocol = SmartClientRequestProtocolOne(medium_request)
421
self._protocol_version = client_protocol.query_version()
457
client_protocol.query_version()
458
self._done_hello = True
422
459
except errors.SmartProtocolError, e:
423
460
# Cache the error, just like we would cache a successful
425
462
self._protocol_version_error = e
427
return self._protocol_version
466
def should_probe(self):
467
"""Should RemoteBzrDirFormat.probe_transport send a smart request on
470
Some transports are unambiguously smart-only; there's no need to check
471
if the transport is able to carry smart requests, because that's all
472
it is for. In those cases, this method should return False.
474
But some HTTP transports can sometimes fail to carry smart requests,
475
but still be usuable for accessing remote bzrdirs via plain file
476
accesses. So for those transports, their media should return True here
477
so that RemoteBzrDirFormat can determine if it is appropriate for that
429
482
def disconnect(self):
430
483
"""If this medium maintains a persistent connection, close it.
432
485
The default implementation does nothing.
488
def remote_path_from_transport(self, transport):
489
"""Convert transport into a path suitable for using in a request.
491
Note that the resulting remote path doesn't encode the host name or
492
anything but path, so it is only safe to use it in requests sent over
493
the medium from the matching transport.
495
medium_base = urlutils.join(self.base, '/')
496
rel_url = urlutils.relative_url(medium_base, transport.base)
497
return urllib.unquote(rel_url)
436
500
class SmartClientStreamMedium(SmartClientMedium):
437
501
"""Stream based medium common class.
446
SmartClientMedium.__init__(self)
509
def __init__(self, base):
510
SmartClientMedium.__init__(self, base)
447
511
self._current_request = None
448
# Be optimistic: we assume the remote end can accept new remote
449
# requests until we get an error saying otherwise. (1.2 adds some
450
# requests that send bodies, which confuses older servers.)
451
self._remote_is_at_least_1_2 = True
453
513
def accept_bytes(self, bytes):
454
514
self._accept_bytes(bytes)
485
545
This client does not manage the pipes: it assumes they will always be open.
488
def __init__(self, readable_pipe, writeable_pipe):
489
SmartClientStreamMedium.__init__(self)
548
def __init__(self, readable_pipe, writeable_pipe, base):
549
SmartClientStreamMedium.__init__(self, base)
490
550
self._readable_pipe = readable_pipe
491
551
self._writeable_pipe = writeable_pipe
507
567
"""A client medium using SSH."""
509
569
def __init__(self, host, port=None, username=None, password=None,
510
vendor=None, bzr_remote_path=None):
570
base=None, vendor=None, bzr_remote_path=None):
511
571
"""Creates a client that will connect on the first use.
513
573
:param vendor: An optional override for the ssh vendor to use. See
514
574
bzrlib.transport.ssh for details on ssh vendors.
516
SmartClientStreamMedium.__init__(self)
576
SmartClientStreamMedium.__init__(self, base)
517
577
self._connected = False
518
578
self._host = host
519
579
self._password = password
579
639
class SmartTCPClientMedium(SmartClientStreamMedium):
580
640
"""A client medium using TCP."""
582
def __init__(self, host, port):
642
def __init__(self, host, port, base):
583
643
"""Creates a client that will connect on the first use."""
584
SmartClientStreamMedium.__init__(self)
644
SmartClientStreamMedium.__init__(self, base)
585
645
self._connected = False
586
646
self._host = host
587
647
self._port = port