~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/medium.py

Merge sftp-leaks into catch-them-all resolving conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""The 'medium' layer for the smart servers and clients.
18
18
 
24
24
bzrlib/transport/smart/__init__.py.
25
25
"""
26
26
 
27
 
import errno
28
27
import os
29
 
import socket
30
28
import sys
31
29
import urllib
32
30
 
 
31
import bzrlib
33
32
from bzrlib.lazy_import import lazy_import
34
33
lazy_import(globals(), """
35
 
import atexit
 
34
import socket
 
35
import thread
36
36
import weakref
 
37
 
37
38
from bzrlib import (
38
39
    debug,
39
40
    errors,
40
 
    osutils,
41
41
    symbol_versioning,
42
42
    trace,
 
43
    ui,
43
44
    urlutils,
44
45
    )
45
 
from bzrlib.smart import client, protocol
 
46
from bzrlib.smart import client, protocol, request, vfs
46
47
from bzrlib.transport import ssh
47
48
""")
48
 
 
49
 
 
50
 
# We must not read any more than 64k at a time so we don't risk "no buffer
51
 
# space available" errors on some platforms.  Windows in particular is likely
52
 
# to give error 10053 or 10055 if we read more than 64k from a socket.
53
 
_MAX_READ_SIZE = 64 * 1024
54
 
 
 
49
from bzrlib import osutils
 
50
 
 
51
# Throughout this module buffer size parameters are either limited to be at
 
52
# most _MAX_READ_SIZE, or are ignored and _MAX_READ_SIZE is used instead.
 
53
# For this module's purposes, MAX_SOCKET_CHUNK is a reasonable size for reads
 
54
# from non-sockets as well.
 
55
_MAX_READ_SIZE = osutils.MAX_SOCKET_CHUNK
55
56
 
56
57
def _get_protocol_factory_for_bytes(bytes):
57
58
    """Determine the right protocol factory for 'bytes'.
87
88
 
88
89
def _get_line(read_bytes_func):
89
90
    """Read bytes using read_bytes_func until a newline byte.
90
 
    
 
91
 
91
92
    This isn't particularly efficient, so should only be used when the
92
93
    expected size of the line is quite short.
93
 
    
 
94
 
94
95
    :returns: a tuple of two strs: (line, excess)
95
96
    """
96
97
    newline_pos = -1
112
113
 
113
114
    def __init__(self):
114
115
        self._push_back_buffer = None
115
 
        
 
116
 
116
117
    def _push_back(self, bytes):
117
118
        """Return unused bytes to the medium, because they belong to the next
118
119
        request(s).
152
153
 
153
154
    def _get_line(self):
154
155
        """Read bytes from this request's response until a newline byte.
155
 
        
 
156
 
156
157
        This isn't particularly efficient, so should only be used when the
157
158
        expected size of the line is quite short.
158
159
 
161
162
        line, excess = _get_line(self.read_bytes)
162
163
        self._push_back(excess)
163
164
        return line
164
 
 
 
165
 
 
166
    def _report_activity(self, bytes, direction):
 
167
        """Notify that this medium has activity.
 
168
 
 
169
        Implementations should call this from all methods that actually do IO.
 
170
        Be careful that it's not called twice, if one method is implemented on
 
171
        top of another.
 
172
 
 
173
        :param bytes: Number of bytes read or written.
 
174
        :param direction: 'read' or 'write' or None.
 
175
        """
 
176
        ui.ui_factory.report_transport_activity(self, bytes, direction)
 
177
 
165
178
 
166
179
class SmartServerStreamMedium(SmartMedium):
167
180
    """Handles smart commands coming over a stream.
172
185
    One instance is created for each connected client; it can serve multiple
173
186
    requests in the lifetime of the connection.
174
187
 
175
 
    The server passes requests through to an underlying backing transport, 
 
188
    The server passes requests through to an underlying backing transport,
176
189
    which will typically be a LocalTransport looking at the server's filesystem.
177
190
 
178
191
    :ivar _push_back_buffer: a str of bytes that have been read from the stream
223
236
 
224
237
    def _serve_one_request(self, protocol):
225
238
        """Read one request from input, process, send back a response.
226
 
        
 
239
 
227
240
        :param protocol: a SmartServerRequestProtocol.
228
241
        """
229
242
        try:
261
274
    def _serve_one_request_unguarded(self, protocol):
262
275
        while protocol.next_read_size():
263
276
            # We can safely try to read large chunks.  If there is less data
264
 
            # than _MAX_READ_SIZE ready, the socket wil just return a short
265
 
            # read immediately rather than block.
266
 
            bytes = self.read_bytes(_MAX_READ_SIZE)
 
277
            # than MAX_SOCKET_CHUNK ready, the socket will just return a
 
278
            # short read immediately rather than block.
 
279
            bytes = self.read_bytes(osutils.MAX_SOCKET_CHUNK)
267
280
            if bytes == '':
268
281
                self.finished = True
269
282
                return
270
283
            protocol.accept_bytes(bytes)
271
 
        
 
284
 
272
285
        self._push_back(protocol.unused_data)
273
286
 
274
287
    def _read_bytes(self, desired_count):
275
 
        # We ignore the desired_count because on sockets it's more efficient to
276
 
        # read large chunks (of _MAX_READ_SIZE bytes) at a time.
277
 
        return osutils.until_no_eintr(self.socket.recv, _MAX_READ_SIZE)
 
288
        return osutils.read_bytes_from_socket(
 
289
            self.socket, self._report_activity)
278
290
 
279
291
    def terminate_due_to_error(self):
280
292
        # TODO: This should log to a server log file, but no such thing
283
295
        self.finished = True
284
296
 
285
297
    def _write_out(self, bytes):
286
 
        osutils.send_all(self.socket, bytes)
 
298
        tstart = osutils.timer_func()
 
299
        osutils.send_all(self.socket, bytes, self._report_activity)
 
300
        if 'hpss' in debug.debug_flags:
 
301
            thread_id = thread.get_ident()
 
302
            trace.mutter('%12s: [%s] %d bytes to the socket in %.3fs'
 
303
                         % ('wrote', thread_id, len(bytes),
 
304
                            osutils.timer_func() - tstart))
287
305
 
288
306
 
289
307
class SmartServerPipeStreamMedium(SmartServerStreamMedium):
350
368
    request.finished_reading()
351
369
 
352
370
    It is up to the individual SmartClientMedium whether multiple concurrent
353
 
    requests can exist. See SmartClientMedium.get_request to obtain instances 
354
 
    of SmartClientMediumRequest, and the concrete Medium you are using for 
 
371
    requests can exist. See SmartClientMedium.get_request to obtain instances
 
372
    of SmartClientMediumRequest, and the concrete Medium you are using for
355
373
    details on concurrency and pipelining.
356
374
    """
357
375
 
366
384
    def accept_bytes(self, bytes):
367
385
        """Accept bytes for inclusion in this request.
368
386
 
369
 
        This method may not be be called after finished_writing() has been
 
387
        This method may not be called after finished_writing() has been
370
388
        called.  It depends upon the Medium whether or not the bytes will be
371
389
        immediately transmitted. Message based Mediums will tend to buffer the
372
390
        bytes until finished_writing() is called.
403
421
    def _finished_reading(self):
404
422
        """Helper for finished_reading.
405
423
 
406
 
        finished_reading checks the state of the request to determine if 
 
424
        finished_reading checks the state of the request to determine if
407
425
        finished_reading is allowed, and if it is hands off to _finished_reading
408
426
        to perform the action.
409
427
        """
423
441
    def _finished_writing(self):
424
442
        """Helper for finished_writing.
425
443
 
426
 
        finished_writing checks the state of the request to determine if 
 
444
        finished_writing checks the state of the request to determine if
427
445
        finished_writing is allowed, and if it is hands off to _finished_writing
428
446
        to perform the action.
429
447
        """
449
467
        read_bytes checks the state of the request to determing if bytes
450
468
        should be read. After that it hands off to _read_bytes to do the
451
469
        actual read.
452
 
        
 
470
 
453
471
        By default this forwards to self._medium.read_bytes because we are
454
472
        operating on the medium's stream.
455
473
        """
460
478
        if not line.endswith('\n'):
461
479
            # end of file encountered reading from server
462
480
            raise errors.ConnectionReset(
463
 
                "please check connectivity and permissions",
464
 
                "(and try -Dhpss if further diagnosis is required)")
 
481
                "Unexpected end of message. Please check connectivity "
 
482
                "and permissions, and report a bug if problems persist.")
465
483
        return line
466
484
 
467
485
    def _read_line(self):
468
486
        """Helper for SmartClientMediumRequest.read_line.
469
 
        
 
487
 
470
488
        By default this forwards to self._medium._get_line because we are
471
489
        operating on the medium's stream.
472
490
        """
476
494
class _DebugCounter(object):
477
495
    """An object that counts the HPSS calls made to each client medium.
478
496
 
479
 
    When a medium is garbage-collected, or failing that when atexit functions
480
 
    are run, the total number of calls made on that medium are reported via
481
 
    trace.note.
 
497
    When a medium is garbage-collected, or failing that when
 
498
    bzrlib.global_state exits, the total number of calls made on that medium
 
499
    are reported via trace.note.
482
500
    """
483
501
 
484
502
    def __init__(self):
485
503
        self.counts = weakref.WeakKeyDictionary()
486
504
        client._SmartClient.hooks.install_named_hook(
487
505
            'call', self.increment_call_count, 'hpss call counter')
488
 
        atexit.register(self.flush_all)
 
506
        bzrlib.global_state.cleanups.add_cleanup(self.flush_all)
489
507
 
490
508
    def track(self, medium):
491
509
        """Start tracking calls made to a medium.
495
513
        """
496
514
        medium_repr = repr(medium)
497
515
        # Add this medium to the WeakKeyDictionary
498
 
        self.counts[medium] = [0, medium_repr]
 
516
        self.counts[medium] = dict(count=0, vfs_count=0,
 
517
                                   medium_repr=medium_repr)
499
518
        # Weakref callbacks are fired in reverse order of their association
500
519
        # with the referenced object.  So we add a weakref *after* adding to
501
520
        # the WeakKeyDict so that we can report the value from it before the
505
524
    def increment_call_count(self, params):
506
525
        # Increment the count in the WeakKeyDictionary
507
526
        value = self.counts[params.medium]
508
 
        value[0] += 1
 
527
        value['count'] += 1
 
528
        try:
 
529
            request_method = request.request_handlers.get(params.method)
 
530
        except KeyError:
 
531
            # A method we don't know about doesn't count as a VFS method.
 
532
            return
 
533
        if issubclass(request_method, vfs.VfsRequest):
 
534
            value['vfs_count'] += 1
509
535
 
510
536
    def done(self, ref):
511
537
        value = self.counts[ref]
512
 
        count, medium_repr = value
 
538
        count, vfs_count, medium_repr = (
 
539
            value['count'], value['vfs_count'], value['medium_repr'])
513
540
        # In case this callback is invoked for the same ref twice (by the
514
541
        # weakref callback and by the atexit function), set the call count back
515
542
        # to 0 so this item won't be reported twice.
516
 
        value[0] = 0
 
543
        value['count'] = 0
 
544
        value['vfs_count'] = 0
517
545
        if count != 0:
518
 
            trace.note('HPSS calls: %d %s', count, medium_repr)
519
 
        
 
546
            trace.note('HPSS calls: %d (%d vfs) %s',
 
547
                       count, vfs_count, medium_repr)
 
548
 
520
549
    def flush_all(self):
521
550
        for ref in list(self.counts.keys()):
522
551
            self.done(ref)
523
552
 
524
553
_debug_counter = None
525
 
  
526
 
  
 
554
 
 
555
 
527
556
class SmartClientMedium(SmartMedium):
528
557
    """Smart client is a medium for sending smart protocol requests over."""
529
558
 
574
603
        """
575
604
        if (self._remote_version_is_before is not None and
576
605
            version_tuple > self._remote_version_is_before):
577
 
            raise AssertionError(
 
606
            # We have been told that the remote side is older than some version
 
607
            # which is newer than a previously supplied older-than version.
 
608
            # This indicates that some smart verb call is not guarded
 
609
            # appropriately (it should simply not have been tried).
 
610
            trace.mutter(
578
611
                "_remember_remote_is_before(%r) called, but "
579
612
                "_remember_remote_is_before(%r) was called previously."
580
 
                % (version_tuple, self._remote_version_is_before))
 
613
                , version_tuple, self._remote_version_is_before)
 
614
            if 'hpss' in debug.debug_flags:
 
615
                ui.ui_factory.show_warning(
 
616
                    "_remember_remote_is_before(%r) called, but "
 
617
                    "_remember_remote_is_before(%r) was called previously."
 
618
                    % (version_tuple, self._remote_version_is_before))
 
619
            return
581
620
        self._remote_version_is_before = version_tuple
582
621
 
583
622
    def protocol_version(self):
617
656
 
618
657
    def disconnect(self):
619
658
        """If this medium maintains a persistent connection, close it.
620
 
        
 
659
 
621
660
        The default implementation does nothing.
622
661
        """
623
 
        
 
662
 
624
663
    def remote_path_from_transport(self, transport):
625
664
        """Convert transport into a path suitable for using in a request.
626
 
        
 
665
 
627
666
        Note that the resulting remote path doesn't encode the host name or
628
667
        anything but path, so it is only safe to use it in requests sent over
629
668
        the medium from the matching transport.
657
696
 
658
697
    def _flush(self):
659
698
        """Flush the output stream.
660
 
        
 
699
 
661
700
        This method is used by the SmartClientStreamMediumRequest to ensure that
662
701
        all data for a request is sent, to avoid long timeouts or deadlocks.
663
702
        """
674
713
 
675
714
class SmartSimplePipesClientMedium(SmartClientStreamMedium):
676
715
    """A client medium using simple pipes.
677
 
    
 
716
 
678
717
    This client does not manage the pipes: it assumes they will always be open.
679
718
    """
680
719
 
686
725
    def _accept_bytes(self, bytes):
687
726
        """See SmartClientStreamMedium.accept_bytes."""
688
727
        self._writeable_pipe.write(bytes)
 
728
        self._report_activity(len(bytes), 'write')
689
729
 
690
730
    def _flush(self):
691
731
        """See SmartClientStreamMedium._flush()."""
693
733
 
694
734
    def _read_bytes(self, count):
695
735
        """See SmartClientStreamMedium._read_bytes."""
696
 
        return self._readable_pipe.read(count)
 
736
        bytes_to_read = min(count, _MAX_READ_SIZE)
 
737
        bytes = self._readable_pipe.read(bytes_to_read)
 
738
        self._report_activity(len(bytes), 'read')
 
739
        return bytes
 
740
 
 
741
 
 
742
class SSHParams(object):
 
743
    """A set of parameters for starting a remote bzr via SSH."""
 
744
 
 
745
    def __init__(self, host, port=None, username=None, password=None,
 
746
            bzr_remote_path='bzr'):
 
747
        self.host = host
 
748
        self.port = port
 
749
        self.username = username
 
750
        self.password = password
 
751
        self.bzr_remote_path = bzr_remote_path
697
752
 
698
753
 
699
754
class SmartSSHClientMedium(SmartClientStreamMedium):
700
 
    """A client medium using SSH."""
 
755
    """A client medium using SSH.
701
756
    
702
 
    def __init__(self, host, port=None, username=None, password=None,
703
 
            base=None, vendor=None, bzr_remote_path=None):
 
757
    It delegates IO to a SmartClientSocketMedium or
 
758
    SmartClientAlreadyConnectedSocketMedium (depending on platform).
 
759
    """
 
760
 
 
761
    def __init__(self, base, ssh_params, vendor=None):
704
762
        """Creates a client that will connect on the first use.
705
 
        
 
763
 
 
764
        :param ssh_params: A SSHParams instance.
706
765
        :param vendor: An optional override for the ssh vendor to use. See
707
766
            bzrlib.transport.ssh for details on ssh vendors.
708
767
        """
 
768
        self._real_medium = None
 
769
        self._ssh_params = ssh_params
 
770
        # for the benefit of progress making a short description of this
 
771
        # transport
 
772
        self._scheme = 'bzr+ssh'
 
773
        # SmartClientStreamMedium stores the repr of this object in its
 
774
        # _DebugCounter so we have to store all the values used in our repr
 
775
        # method before calling the super init.
709
776
        SmartClientStreamMedium.__init__(self, base)
710
 
        self._connected = False
711
 
        self._host = host
712
 
        self._password = password
713
 
        self._port = port
714
 
        self._username = username
715
 
        self._read_from = None
 
777
        self._vendor = vendor
716
778
        self._ssh_connection = None
717
 
        self._vendor = vendor
718
 
        self._write_to = None
719
 
        self._bzr_remote_path = bzr_remote_path
720
 
        if self._bzr_remote_path is None:
721
 
            symbol_versioning.warn(
722
 
                'bzr_remote_path is required as of bzr 0.92',
723
 
                DeprecationWarning, stacklevel=2)
724
 
            self._bzr_remote_path = os.environ.get('BZR_REMOTE_PATH', 'bzr')
 
779
 
 
780
    def __repr__(self):
 
781
        if self._ssh_params.port is None:
 
782
            maybe_port = ''
 
783
        else:
 
784
            maybe_port = ':%s' % self._ssh_params.port
 
785
        return "%s(%s://%s@%s%s/)" % (
 
786
            self.__class__.__name__,
 
787
            self._scheme,
 
788
            self._ssh_params.username,
 
789
            self._ssh_params.host,
 
790
            maybe_port)
725
791
 
726
792
    def _accept_bytes(self, bytes):
727
793
        """See SmartClientStreamMedium.accept_bytes."""
728
794
        self._ensure_connection()
729
 
        self._write_to.write(bytes)
 
795
        self._real_medium.accept_bytes(bytes)
730
796
 
731
797
    def disconnect(self):
732
798
        """See SmartClientMedium.disconnect()."""
733
 
        if not self._connected:
734
 
            return
735
 
        self._read_from.close()
736
 
        self._write_to.close()
737
 
        self._ssh_connection.close()
738
 
        self._connected = False
 
799
        if self._real_medium is not None:
 
800
            self._real_medium.disconnect()
 
801
            self._real_medium = None
 
802
        if self._ssh_connection is not None:
 
803
            self._ssh_connection.close()
 
804
            self._ssh_connection = None
739
805
 
740
806
    def _ensure_connection(self):
741
807
        """Connect this medium if not already connected."""
742
 
        if self._connected:
 
808
        if self._real_medium is not None:
743
809
            return
744
810
        if self._vendor is None:
745
811
            vendor = ssh._get_ssh_vendor()
746
812
        else:
747
813
            vendor = self._vendor
748
 
        self._ssh_connection = vendor.connect_ssh(self._username,
749
 
                self._password, self._host, self._port,
750
 
                command=[self._bzr_remote_path, 'serve', '--inet',
 
814
        self._ssh_connection = vendor.connect_ssh(self._ssh_params.username,
 
815
                self._ssh_params.password, self._ssh_params.host,
 
816
                self._ssh_params.port,
 
817
                command=[self._ssh_params.bzr_remote_path, 'serve', '--inet',
751
818
                         '--directory=/', '--allow-writes'])
752
 
        self._read_from, self._write_to = \
753
 
            self._ssh_connection.get_filelike_channels()
754
 
        self._connected = True
 
819
        io_kind, io_object = self._ssh_connection.get_sock_or_pipes()
 
820
        if io_kind == 'socket':
 
821
            self._real_medium = SmartClientAlreadyConnectedSocketMedium(
 
822
                self.base, io_object)
 
823
        elif io_kind == 'pipes':
 
824
            read_from, write_to = io_object
 
825
            self._real_medium = SmartSimplePipesClientMedium(
 
826
                read_from, write_to, self.base)
 
827
        else:
 
828
            raise AssertionError(
 
829
                "Unexpected io_kind %r from %r"
 
830
                % (io_kind, self._ssh_connection))
755
831
 
756
832
    def _flush(self):
757
833
        """See SmartClientStreamMedium._flush()."""
758
 
        self._write_to.flush()
 
834
        self._real_medium._flush()
759
835
 
760
836
    def _read_bytes(self, count):
761
837
        """See SmartClientStreamMedium.read_bytes."""
762
 
        if not self._connected:
 
838
        if self._real_medium is None:
763
839
            raise errors.MediumNotConnected(self)
764
 
        bytes_to_read = min(count, _MAX_READ_SIZE)
765
 
        return self._read_from.read(bytes_to_read)
 
840
        return self._real_medium.read_bytes(count)
766
841
 
767
842
 
768
843
# Port 4155 is the default port for bzr://, registered with IANA.
770
845
BZR_DEFAULT_PORT = 4155
771
846
 
772
847
 
773
 
class SmartTCPClientMedium(SmartClientStreamMedium):
774
 
    """A client medium using TCP."""
 
848
class SmartClientSocketMedium(SmartClientStreamMedium):
 
849
    """A client medium using a socket.
775
850
    
 
851
    This class isn't usable directly.  Use one of its subclasses instead.
 
852
    """
 
853
 
 
854
    def __init__(self, base):
 
855
        SmartClientStreamMedium.__init__(self, base)
 
856
        self._socket = None
 
857
        self._connected = False
 
858
 
 
859
    def _accept_bytes(self, bytes):
 
860
        """See SmartClientMedium.accept_bytes."""
 
861
        self._ensure_connection()
 
862
        osutils.send_all(self._socket, bytes, self._report_activity)
 
863
 
 
864
    def _ensure_connection(self):
 
865
        """Connect this medium if not already connected."""
 
866
        raise NotImplementedError(self._ensure_connection)
 
867
 
 
868
    def _flush(self):
 
869
        """See SmartClientStreamMedium._flush().
 
870
 
 
871
        For sockets we do no flushing. For TCP sockets we may want to turn off
 
872
        TCP_NODELAY and add a means to do a flush, but that can be done in the
 
873
        future.
 
874
        """
 
875
 
 
876
    def _read_bytes(self, count):
 
877
        """See SmartClientMedium.read_bytes."""
 
878
        if not self._connected:
 
879
            raise errors.MediumNotConnected(self)
 
880
        return osutils.read_bytes_from_socket(
 
881
            self._socket, self._report_activity)
 
882
 
 
883
    def disconnect(self):
 
884
        """See SmartClientMedium.disconnect()."""
 
885
        if not self._connected:
 
886
            return
 
887
        self._socket.close()
 
888
        self._socket = None
 
889
        self._connected = False
 
890
 
 
891
 
 
892
class SmartTCPClientMedium(SmartClientSocketMedium):
 
893
    """A client medium that creates a TCP connection."""
 
894
 
776
895
    def __init__(self, host, port, base):
777
896
        """Creates a client that will connect on the first use."""
778
 
        SmartClientStreamMedium.__init__(self, base)
779
 
        self._connected = False
 
897
        SmartClientSocketMedium.__init__(self, base)
780
898
        self._host = host
781
899
        self._port = port
782
 
        self._socket = None
783
 
 
784
 
    def _accept_bytes(self, bytes):
785
 
        """See SmartClientMedium.accept_bytes."""
786
 
        self._ensure_connection()
787
 
        osutils.send_all(self._socket, bytes)
788
 
 
789
 
    def disconnect(self):
790
 
        """See SmartClientMedium.disconnect()."""
791
 
        if not self._connected:
792
 
            return
793
 
        self._socket.close()
794
 
        self._socket = None
795
 
        self._connected = False
796
900
 
797
901
    def _ensure_connection(self):
798
902
        """Connect this medium if not already connected."""
803
907
        else:
804
908
            port = int(self._port)
805
909
        try:
806
 
            sockaddrs = socket.getaddrinfo(self._host, port, socket.AF_UNSPEC, 
 
910
            sockaddrs = socket.getaddrinfo(self._host, port, socket.AF_UNSPEC,
807
911
                socket.SOCK_STREAM, 0, 0)
808
912
        except socket.gaierror, (err_num, err_msg):
809
913
            raise errors.ConnectionError("failed to lookup %s:%d: %s" %
813
917
        for (family, socktype, proto, canonname, sockaddr) in sockaddrs:
814
918
            try:
815
919
                self._socket = socket.socket(family, socktype, proto)
816
 
                self._socket.setsockopt(socket.IPPROTO_TCP, 
 
920
                self._socket.setsockopt(socket.IPPROTO_TCP,
817
921
                                        socket.TCP_NODELAY, 1)
818
922
                self._socket.connect(sockaddr)
819
923
            except socket.error, err:
833
937
                    (self._host, port, err_msg))
834
938
        self._connected = True
835
939
 
836
 
    def _flush(self):
837
 
        """See SmartClientStreamMedium._flush().
838
 
        
839
 
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and 
840
 
        add a means to do a flush, but that can be done in the future.
841
 
        """
842
 
 
843
 
    def _read_bytes(self, count):
844
 
        """See SmartClientMedium.read_bytes."""
845
 
        if not self._connected:
846
 
            raise errors.MediumNotConnected(self)
847
 
        # We ignore the desired_count because on sockets it's more efficient to
848
 
        # read large chunks (of _MAX_READ_SIZE bytes) at a time.
849
 
        try:
850
 
            return self._socket.recv(_MAX_READ_SIZE)
851
 
        except socket.error, e:
852
 
            if len(e.args) and e.args[0] == errno.ECONNRESET:
853
 
                # Callers expect an empty string in that case
854
 
                return ''
855
 
            else:
856
 
                raise
 
940
 
 
941
class SmartClientAlreadyConnectedSocketMedium(SmartClientSocketMedium):
 
942
    """A client medium for an already connected socket.
 
943
    
 
944
    Note that this class will assume it "owns" the socket, so it will close it
 
945
    when its disconnect method is called.
 
946
    """
 
947
 
 
948
    def __init__(self, base, sock):
 
949
        SmartClientSocketMedium.__init__(self, base)
 
950
        self._socket = sock
 
951
        self._connected = True
 
952
 
 
953
    def _ensure_connection(self):
 
954
        # Already connected, by definition!  So nothing to do.
 
955
        pass
857
956
 
858
957
 
859
958
class SmartClientStreamMediumRequest(SmartClientMediumRequest):
872
971
 
873
972
    def _accept_bytes(self, bytes):
874
973
        """See SmartClientMediumRequest._accept_bytes.
875
 
        
 
974
 
876
975
        This forwards to self._medium._accept_bytes because we are operating
877
976
        on the mediums stream.
878
977
        """
881
980
    def _finished_reading(self):
882
981
        """See SmartClientMediumRequest._finished_reading.
883
982
 
884
 
        This clears the _current_request on self._medium to allow a new 
 
983
        This clears the _current_request on self._medium to allow a new
885
984
        request to be created.
886
985
        """
887
986
        if self._medium._current_request is not self:
888
987
            raise AssertionError()
889
988
        self._medium._current_request = None
890
 
        
 
989
 
891
990
    def _finished_writing(self):
892
991
        """See SmartClientMediumRequest._finished_writing.
893
992
 
895
994
        """
896
995
        self._medium._flush()
897
996
 
 
997