/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1
# Copyright (C) 2006 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
17
"""The 'medium' layer for the smart servers and clients.
18
19
"Medium" here is the noun meaning "a means of transmission", not the adjective
20
for "the quality between big and small."
21
22
Media carry the bytes of the requests somehow (e.g. via TCP, wrapped in HTTP, or
23
over SSH), and pass them to and from the protocol logic.  See the overview in
24
bzrlib/transport/smart/__init__.py.
25
"""
26
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
27
import os
28
import socket
2018.5.162 by Andrew Bennetts
Add some missing _ensure_real calls, and a missing import.
29
import sys
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
30
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
31
from bzrlib import (
32
    errors,
3118.2.1 by Andrew Bennetts
(andrew) Fix #115781 by passing no more than 64k at a time to socket.sendall.
33
    osutils,
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
34
    symbol_versioning,
35
    )
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
36
from bzrlib.smart.protocol import (
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
37
    REQUEST_VERSION_TWO,
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
38
    SmartServerRequestProtocolOne,
39
    SmartServerRequestProtocolTwo,
40
    )
3066.2.1 by John Arbash Meinel
We don't require paramiko for bzr+ssh.
41
from bzrlib.transport import ssh
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
42
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
43
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
44
class SmartServerStreamMedium(object):
45
    """Handles smart commands coming over a stream.
46
47
    The stream may be a pipe connected to sshd, or a tcp socket, or an
48
    in-process fifo for testing.
49
50
    One instance is created for each connected client; it can serve multiple
51
    requests in the lifetime of the connection.
52
53
    The server passes requests through to an underlying backing transport, 
54
    which will typically be a LocalTransport looking at the server's filesystem.
55
    """
56
57
    def __init__(self, backing_transport):
58
        """Construct new server.
59
60
        :param backing_transport: Transport for the directory served.
61
        """
62
        # backing_transport could be passed to serve instead of __init__
63
        self.backing_transport = backing_transport
64
        self.finished = False
3236.3.2 by Andrew Bennetts
Fix SmartServerPipeStreamMedium._get_line too.
65
        self.push_back = None
66
67
    def _push_back(self, bytes):
68
        assert self.push_back is None, (
69
            "_push_back called when self.push is %r" % (self.push_back,))
70
        if bytes == '':
71
            return
72
        self.push_back = bytes
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
73
74
    def serve(self):
75
        """Serve requests until the client disconnects."""
76
        # Keep a reference to stderr because the sys module's globals get set to
77
        # None during interpreter shutdown.
78
        from sys import stderr
79
        try:
80
            while not self.finished:
2432.2.3 by Andrew Bennetts
Merge from bzr.dev.
81
                server_protocol = self._build_protocol()
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
82
                self._serve_one_request(server_protocol)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
83
        except Exception, e:
84
            stderr.write("%s terminating on exception %s\n" % (self, e))
85
            raise
86
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
87
    def _build_protocol(self):
2432.2.8 by Andrew Bennetts
NEWS entry, greatly improved docstring in bzrlib.smart.
88
        """Identifies the version of the incoming request, and returns an
89
        a protocol object that can interpret it.
90
91
        If more bytes than the version prefix of the request are read, they will
92
        be fed into the protocol before it is returned.
93
94
        :returns: a SmartServerRequestProtocol.
95
        """
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
96
        # Identify the protocol version.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
97
        bytes = self._get_line()
98
        if bytes.startswith(REQUEST_VERSION_TWO):
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
99
            protocol_class = SmartServerRequestProtocolTwo
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
100
            bytes = bytes[len(REQUEST_VERSION_TWO):]
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
101
        else:
102
            protocol_class = SmartServerRequestProtocolOne
103
        protocol = protocol_class(self.backing_transport, self._write_out)
104
        protocol.accept_bytes(bytes)
105
        return protocol
106
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
107
    def _serve_one_request(self, protocol):
108
        """Read one request from input, process, send back a response.
109
        
110
        :param protocol: a SmartServerRequestProtocol.
111
        """
112
        try:
113
            self._serve_one_request_unguarded(protocol)
114
        except KeyboardInterrupt:
115
            raise
116
        except Exception, e:
117
            self.terminate_due_to_error()
118
119
    def terminate_due_to_error(self):
120
        """Called when an unhandled exception from the protocol occurs."""
121
        raise NotImplementedError(self.terminate_due_to_error)
122
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
123
    def _get_bytes(self, desired_count):
124
        """Get some bytes from the medium.
125
126
        :param desired_count: number of bytes we want to read.
127
        """
128
        raise NotImplementedError(self._get_bytes)
129
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
130
    def _get_line(self):
131
        """Read bytes from this request's response until a newline byte.
132
        
133
        This isn't particularly efficient, so should only be used when the
134
        expected size of the line is quite short.
135
136
        :returns: a string of bytes ending in a newline (byte 0x0A).
137
        """
3236.3.2 by Andrew Bennetts
Fix SmartServerPipeStreamMedium._get_line too.
138
        newline_pos = -1
139
        bytes = ''
140
        while newline_pos == -1:
141
            new_bytes = self._get_bytes(1)
142
            bytes += new_bytes
143
            if new_bytes == '':
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
144
                # Ran out of bytes before receiving a complete line.
3236.3.2 by Andrew Bennetts
Fix SmartServerPipeStreamMedium._get_line too.
145
                return bytes
146
            newline_pos = bytes.find('\n')
147
        line = bytes[:newline_pos+1]
148
        self._push_back(bytes[newline_pos+1:])
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
149
        return line
3236.3.2 by Andrew Bennetts
Fix SmartServerPipeStreamMedium._get_line too.
150
 
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
151
152
class SmartServerSocketStreamMedium(SmartServerStreamMedium):
153
154
    def __init__(self, sock, backing_transport):
155
        """Constructor.
156
157
        :param sock: the socket the server will read from.  It will be put
158
            into blocking mode.
159
        """
160
        SmartServerStreamMedium.__init__(self, backing_transport)
161
        sock.setblocking(True)
162
        self.socket = sock
163
164
    def _serve_one_request_unguarded(self, protocol):
165
        while protocol.next_read_size():
166
            if self.push_back:
167
                protocol.accept_bytes(self.push_back)
3236.3.1 by Andrew Bennetts
Fix a bug in SmartServerSocketStreamMedium._get_line, and add some asserts to catch this sort of mistake sooner.
168
                self.push_back = None
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
169
            else:
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
170
                bytes = self._get_bytes(4096)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
171
                if bytes == '':
172
                    self.finished = True
173
                    return
174
                protocol.accept_bytes(bytes)
175
        
3236.3.1 by Andrew Bennetts
Fix a bug in SmartServerSocketStreamMedium._get_line, and add some asserts to catch this sort of mistake sooner.
176
        self._push_back(protocol.excess_buffer)
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
177
178
    def _get_bytes(self, desired_count):
179
        # We ignore the desired_count because on sockets it's more efficient to
180
        # read 4k at a time.
3236.3.1 by Andrew Bennetts
Fix a bug in SmartServerSocketStreamMedium._get_line, and add some asserts to catch this sort of mistake sooner.
181
        if self.push_back is not None:
182
            assert self.push_back != '', (
183
                'self.push_back should never be the empty string, which can be '
184
                'confused with EOF')
185
            bytes = self.push_back
186
            self.push_back = None
187
            return bytes
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
188
        return self.socket.recv(4096)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
189
    
190
    def terminate_due_to_error(self):
191
        """Called when an unhandled exception from the protocol occurs."""
192
        # TODO: This should log to a server log file, but no such thing
193
        # exists yet.  Andrew Bennetts 2006-09-29.
194
        self.socket.close()
195
        self.finished = True
196
197
    def _write_out(self, bytes):
3118.2.1 by Andrew Bennetts
(andrew) Fix #115781 by passing no more than 64k at a time to socket.sendall.
198
        osutils.send_all(self.socket, bytes)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
199
200
201
class SmartServerPipeStreamMedium(SmartServerStreamMedium):
202
203
    def __init__(self, in_file, out_file, backing_transport):
204
        """Construct new server.
205
206
        :param in_file: Python file from which requests can be read.
207
        :param out_file: Python file to write responses.
208
        :param backing_transport: Transport for the directory served.
209
        """
210
        SmartServerStreamMedium.__init__(self, backing_transport)
2018.5.161 by Andrew Bennetts
Reinstate forcing binary mode on windows in SmartServerStreamMedium.
211
        if sys.platform == 'win32':
212
            # force binary mode for files
213
            import msvcrt
214
            for f in (in_file, out_file):
215
                fileno = getattr(f, 'fileno', None)
216
                if fileno:
217
                    msvcrt.setmode(fileno(), os.O_BINARY)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
218
        self._in = in_file
219
        self._out = out_file
220
221
    def _serve_one_request_unguarded(self, protocol):
222
        while True:
223
            bytes_to_read = protocol.next_read_size()
224
            if bytes_to_read == 0:
225
                # Finished serving this request.
226
                self._out.flush()
227
                return
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
228
            bytes = self._get_bytes(bytes_to_read)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
229
            if bytes == '':
230
                # Connection has been closed.
231
                self.finished = True
232
                self._out.flush()
233
                return
234
            protocol.accept_bytes(bytes)
235
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
236
    def _get_bytes(self, desired_count):
3236.3.2 by Andrew Bennetts
Fix SmartServerPipeStreamMedium._get_line too.
237
        if self.push_back is not None:
238
            assert self.push_back != '', (
239
                'self.push_back should never be the empty string, which can be '
240
                'confused with EOF')
241
            bytes = self.push_back
242
            self.push_back = None
243
            return bytes
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
244
        return self._in.read(desired_count)
245
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
246
    def terminate_due_to_error(self):
247
        # TODO: This should log to a server log file, but no such thing
248
        # exists yet.  Andrew Bennetts 2006-09-29.
249
        self._out.close()
250
        self.finished = True
251
252
    def _write_out(self, bytes):
253
        self._out.write(bytes)
254
255
256
class SmartClientMediumRequest(object):
257
    """A request on a SmartClientMedium.
258
259
    Each request allows bytes to be provided to it via accept_bytes, and then
260
    the response bytes to be read via read_bytes.
261
262
    For instance:
263
    request.accept_bytes('123')
264
    request.finished_writing()
265
    result = request.read_bytes(3)
266
    request.finished_reading()
267
268
    It is up to the individual SmartClientMedium whether multiple concurrent
269
    requests can exist. See SmartClientMedium.get_request to obtain instances 
270
    of SmartClientMediumRequest, and the concrete Medium you are using for 
271
    details on concurrency and pipelining.
272
    """
273
274
    def __init__(self, medium):
275
        """Construct a SmartClientMediumRequest for the medium medium."""
276
        self._medium = medium
277
        # we track state by constants - we may want to use the same
278
        # pattern as BodyReader if it gets more complex.
279
        # valid states are: "writing", "reading", "done"
280
        self._state = "writing"
281
282
    def accept_bytes(self, bytes):
283
        """Accept bytes for inclusion in this request.
284
285
        This method may not be be called after finished_writing() has been
286
        called.  It depends upon the Medium whether or not the bytes will be
287
        immediately transmitted. Message based Mediums will tend to buffer the
288
        bytes until finished_writing() is called.
289
290
        :param bytes: A bytestring.
291
        """
292
        if self._state != "writing":
293
            raise errors.WritingCompleted(self)
294
        self._accept_bytes(bytes)
295
296
    def _accept_bytes(self, bytes):
297
        """Helper for accept_bytes.
298
299
        Accept_bytes checks the state of the request to determing if bytes
300
        should be accepted. After that it hands off to _accept_bytes to do the
301
        actual acceptance.
302
        """
303
        raise NotImplementedError(self._accept_bytes)
304
305
    def finished_reading(self):
306
        """Inform the request that all desired data has been read.
307
308
        This will remove the request from the pipeline for its medium (if the
309
        medium supports pipelining) and any further calls to methods on the
310
        request will raise ReadingCompleted.
311
        """
312
        if self._state == "writing":
313
            raise errors.WritingNotComplete(self)
314
        if self._state != "reading":
315
            raise errors.ReadingCompleted(self)
316
        self._state = "done"
317
        self._finished_reading()
318
319
    def _finished_reading(self):
320
        """Helper for finished_reading.
321
322
        finished_reading checks the state of the request to determine if 
323
        finished_reading is allowed, and if it is hands off to _finished_reading
324
        to perform the action.
325
        """
326
        raise NotImplementedError(self._finished_reading)
327
328
    def finished_writing(self):
329
        """Finish the writing phase of this request.
330
331
        This will flush all pending data for this request along the medium.
332
        After calling finished_writing, you may not call accept_bytes anymore.
333
        """
334
        if self._state != "writing":
335
            raise errors.WritingCompleted(self)
336
        self._state = "reading"
337
        self._finished_writing()
338
339
    def _finished_writing(self):
340
        """Helper for finished_writing.
341
342
        finished_writing checks the state of the request to determine if 
343
        finished_writing is allowed, and if it is hands off to _finished_writing
344
        to perform the action.
345
        """
346
        raise NotImplementedError(self._finished_writing)
347
348
    def read_bytes(self, count):
349
        """Read bytes from this requests response.
350
351
        This method will block and wait for count bytes to be read. It may not
352
        be invoked until finished_writing() has been called - this is to ensure
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
353
        a message-based approach to requests, for compatibility with message
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
354
        based mediums like HTTP.
355
        """
356
        if self._state == "writing":
357
            raise errors.WritingNotComplete(self)
358
        if self._state != "reading":
359
            raise errors.ReadingCompleted(self)
360
        return self._read_bytes(count)
361
362
    def _read_bytes(self, count):
363
        """Helper for read_bytes.
364
365
        read_bytes checks the state of the request to determing if bytes
366
        should be read. After that it hands off to _read_bytes to do the
367
        actual read.
368
        """
369
        raise NotImplementedError(self._read_bytes)
370
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
371
    def read_line(self):
372
        """Read bytes from this request's response until a newline byte.
373
        
374
        This isn't particularly efficient, so should only be used when the
375
        expected size of the line is quite short.
376
377
        :returns: a string of bytes ending in a newline (byte 0x0A).
378
        """
379
        # XXX: this duplicates SmartClientRequestProtocolOne._recv_tuple
380
        line = ''
381
        while not line or line[-1] != '\n':
382
            new_char = self.read_bytes(1)
383
            line += new_char
384
            if new_char == '':
3195.2.1 by Andrew Bennetts
Improve test coverage, and fix a bug revealed by the improved coverage.
385
                # end of file encountered reading from server
386
                raise errors.ConnectionReset(
387
                    "please check connectivity and permissions",
388
                    "(and try -Dhpss if further diagnosis is required)")
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
389
        return line
390
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
391
392
class SmartClientMedium(object):
393
    """Smart client is a medium for sending smart protocol requests over."""
394
395
    def disconnect(self):
396
        """If this medium maintains a persistent connection, close it.
397
        
398
        The default implementation does nothing.
399
        """
400
        
401
402
class SmartClientStreamMedium(SmartClientMedium):
403
    """Stream based medium common class.
404
405
    SmartClientStreamMediums operate on a stream. All subclasses use a common
406
    SmartClientStreamMediumRequest for their requests, and should implement
407
    _accept_bytes and _read_bytes to allow the request objects to send and
408
    receive bytes.
409
    """
410
411
    def __init__(self):
412
        self._current_request = None
3213.1.1 by Andrew Bennetts
Recover (by reconnecting) if the server turns out not to understand the new requests in 1.2 that send bodies.
413
        # Be optimistic: we assume the remote end can accept new remote
414
        # requests until we get an error saying otherwise.  (1.2 adds some
415
        # requests that send bodies, which confuses older servers.)
416
        self._remote_is_at_least_1_2 = True
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
417
418
    def accept_bytes(self, bytes):
419
        self._accept_bytes(bytes)
420
421
    def __del__(self):
422
        """The SmartClientStreamMedium knows how to close the stream when it is
423
        finished with it.
424
        """
425
        self.disconnect()
426
427
    def _flush(self):
428
        """Flush the output stream.
429
        
430
        This method is used by the SmartClientStreamMediumRequest to ensure that
431
        all data for a request is sent, to avoid long timeouts or deadlocks.
432
        """
433
        raise NotImplementedError(self._flush)
434
435
    def get_request(self):
436
        """See SmartClientMedium.get_request().
437
438
        SmartClientStreamMedium always returns a SmartClientStreamMediumRequest
439
        for get_request.
440
        """
441
        return SmartClientStreamMediumRequest(self)
442
443
    def read_bytes(self, count):
444
        return self._read_bytes(count)
445
446
447
class SmartSimplePipesClientMedium(SmartClientStreamMedium):
448
    """A client medium using simple pipes.
449
    
450
    This client does not manage the pipes: it assumes they will always be open.
451
    """
452
453
    def __init__(self, readable_pipe, writeable_pipe):
454
        SmartClientStreamMedium.__init__(self)
455
        self._readable_pipe = readable_pipe
456
        self._writeable_pipe = writeable_pipe
457
458
    def _accept_bytes(self, bytes):
459
        """See SmartClientStreamMedium.accept_bytes."""
460
        self._writeable_pipe.write(bytes)
461
462
    def _flush(self):
463
        """See SmartClientStreamMedium._flush()."""
464
        self._writeable_pipe.flush()
465
466
    def _read_bytes(self, count):
467
        """See SmartClientStreamMedium._read_bytes."""
468
        return self._readable_pipe.read(count)
469
470
471
class SmartSSHClientMedium(SmartClientStreamMedium):
472
    """A client medium using SSH."""
473
    
474
    def __init__(self, host, port=None, username=None, password=None,
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
475
            vendor=None, bzr_remote_path=None):
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
476
        """Creates a client that will connect on the first use.
477
        
478
        :param vendor: An optional override for the ssh vendor to use. See
479
            bzrlib.transport.ssh for details on ssh vendors.
480
        """
481
        SmartClientStreamMedium.__init__(self)
482
        self._connected = False
483
        self._host = host
484
        self._password = password
485
        self._port = port
486
        self._username = username
487
        self._read_from = None
488
        self._ssh_connection = None
489
        self._vendor = vendor
490
        self._write_to = None
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
491
        self._bzr_remote_path = bzr_remote_path
492
        if self._bzr_remote_path is None:
493
            symbol_versioning.warn(
494
                'bzr_remote_path is required as of bzr 0.92',
495
                DeprecationWarning, stacklevel=2)
496
            self._bzr_remote_path = os.environ.get('BZR_REMOTE_PATH', 'bzr')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
497
498
    def _accept_bytes(self, bytes):
499
        """See SmartClientStreamMedium.accept_bytes."""
500
        self._ensure_connection()
501
        self._write_to.write(bytes)
502
503
    def disconnect(self):
504
        """See SmartClientMedium.disconnect()."""
505
        if not self._connected:
506
            return
507
        self._read_from.close()
508
        self._write_to.close()
509
        self._ssh_connection.close()
510
        self._connected = False
511
512
    def _ensure_connection(self):
513
        """Connect this medium if not already connected."""
514
        if self._connected:
515
            return
516
        if self._vendor is None:
517
            vendor = ssh._get_ssh_vendor()
518
        else:
519
            vendor = self._vendor
520
        self._ssh_connection = vendor.connect_ssh(self._username,
521
                self._password, self._host, self._port,
1551.18.17 by Aaron Bentley
Introduce bzr_remote_path configuration variable
522
                command=[self._bzr_remote_path, 'serve', '--inet',
523
                         '--directory=/', '--allow-writes'])
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
524
        self._read_from, self._write_to = \
525
            self._ssh_connection.get_filelike_channels()
526
        self._connected = True
527
528
    def _flush(self):
529
        """See SmartClientStreamMedium._flush()."""
530
        self._write_to.flush()
531
532
    def _read_bytes(self, count):
533
        """See SmartClientStreamMedium.read_bytes."""
534
        if not self._connected:
535
            raise errors.MediumNotConnected(self)
536
        return self._read_from.read(count)
537
538
3004.2.1 by Vincent Ladeuil
Fix 150860 by leaving port as user specified it.
539
# Port 4155 is the default port for bzr://, registered with IANA.
540
BZR_DEFAULT_INTERFACE = '0.0.0.0'
541
BZR_DEFAULT_PORT = 4155
542
543
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
544
class SmartTCPClientMedium(SmartClientStreamMedium):
545
    """A client medium using TCP."""
546
    
547
    def __init__(self, host, port):
548
        """Creates a client that will connect on the first use."""
549
        SmartClientStreamMedium.__init__(self)
550
        self._connected = False
551
        self._host = host
552
        self._port = port
553
        self._socket = None
554
555
    def _accept_bytes(self, bytes):
556
        """See SmartClientMedium.accept_bytes."""
557
        self._ensure_connection()
3118.2.1 by Andrew Bennetts
(andrew) Fix #115781 by passing no more than 64k at a time to socket.sendall.
558
        osutils.send_all(self._socket, bytes)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
559
560
    def disconnect(self):
561
        """See SmartClientMedium.disconnect()."""
562
        if not self._connected:
563
            return
564
        self._socket.close()
565
        self._socket = None
566
        self._connected = False
567
568
    def _ensure_connection(self):
569
        """Connect this medium if not already connected."""
570
        if self._connected:
571
            return
572
        self._socket = socket.socket()
573
        self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
3004.2.1 by Vincent Ladeuil
Fix 150860 by leaving port as user specified it.
574
        if self._port is None:
575
            port = BZR_DEFAULT_PORT
576
        else:
577
            port = int(self._port)
3180.1.1 by Andrew Bennetts
Don't traceback on host name errors when connecting to bzr://...
578
        try:
579
            self._socket.connect((self._host, port))
580
        except socket.error, err:
581
            # socket errors either have a (string) or (errno, string) as their
582
            # args.
583
            if type(err.args) is str:
584
                err_msg = err.args
585
            else:
586
                err_msg = err.args[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
587
            raise errors.ConnectionError("failed to connect to %s:%d: %s" %
3180.1.1 by Andrew Bennetts
Don't traceback on host name errors when connecting to bzr://...
588
                    (self._host, port, err_msg))
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
589
        self._connected = True
590
591
    def _flush(self):
592
        """See SmartClientStreamMedium._flush().
593
        
594
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and 
595
        add a means to do a flush, but that can be done in the future.
596
        """
597
598
    def _read_bytes(self, count):
599
        """See SmartClientMedium.read_bytes."""
600
        if not self._connected:
601
            raise errors.MediumNotConnected(self)
602
        return self._socket.recv(count)
603
604
605
class SmartClientStreamMediumRequest(SmartClientMediumRequest):
606
    """A SmartClientMediumRequest that works with an SmartClientStreamMedium."""
607
608
    def __init__(self, medium):
609
        SmartClientMediumRequest.__init__(self, medium)
610
        # check that we are safe concurrency wise. If some streams start
611
        # allowing concurrent requests - i.e. via multiplexing - then this
612
        # assert should be moved to SmartClientStreamMedium.get_request,
613
        # and the setting/unsetting of _current_request likewise moved into
614
        # that class : but its unneeded overhead for now. RBC 20060922
615
        if self._medium._current_request is not None:
616
            raise errors.TooManyConcurrentRequests(self._medium)
617
        self._medium._current_request = self
618
619
    def _accept_bytes(self, bytes):
620
        """See SmartClientMediumRequest._accept_bytes.
621
        
622
        This forwards to self._medium._accept_bytes because we are operating
623
        on the mediums stream.
624
        """
625
        self._medium._accept_bytes(bytes)
626
627
    def _finished_reading(self):
628
        """See SmartClientMediumRequest._finished_reading.
629
630
        This clears the _current_request on self._medium to allow a new 
631
        request to be created.
632
        """
633
        assert self._medium._current_request is self
634
        self._medium._current_request = None
635
        
636
    def _finished_writing(self):
637
        """See SmartClientMediumRequest._finished_writing.
638
639
        This invokes self._medium._flush to ensure all bytes are transmitted.
640
        """
641
        self._medium._flush()
642
643
    def _read_bytes(self, count):
644
        """See SmartClientMediumRequest._read_bytes.
645
        
646
        This forwards to self._medium._read_bytes because we are operating
647
        on the mediums stream.
648
        """
649
        return self._medium._read_bytes(count)
650