~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/medium.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-02-18 04:04:19 UTC
  • mfrom: (5035.4.8 335033-upgrade)
  • Revision ID: pqm@pqm.ubuntu.com-20100218040419-l1gkx8dm60czagfq
(mbp, for parthm) copy to backup.bzr.~N~ before upgrading

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
27
28
import os
28
29
import socket
29
30
import sys
31
32
 
32
33
from bzrlib.lazy_import import lazy_import
33
34
lazy_import(globals(), """
 
35
import atexit
 
36
import thread
 
37
import weakref
 
38
 
34
39
from bzrlib import (
 
40
    debug,
35
41
    errors,
36
 
    osutils,
37
42
    symbol_versioning,
 
43
    trace,
 
44
    ui,
38
45
    urlutils,
39
46
    )
40
 
from bzrlib.smart import protocol
 
47
from bzrlib.smart import client, protocol, request, vfs
41
48
from bzrlib.transport import ssh
42
49
""")
43
 
 
 
50
#usually already imported, and getting IllegalScoperReplacer on it here.
 
51
from bzrlib import osutils
44
52
 
45
53
# We must not read any more than 64k at a time so we don't risk "no buffer
46
54
# space available" errors on some platforms.  Windows in particular is likely
82
90
 
83
91
def _get_line(read_bytes_func):
84
92
    """Read bytes using read_bytes_func until a newline byte.
85
 
    
 
93
 
86
94
    This isn't particularly efficient, so should only be used when the
87
95
    expected size of the line is quite short.
88
 
    
 
96
 
89
97
    :returns: a tuple of two strs: (line, excess)
90
98
    """
91
99
    newline_pos = -1
107
115
 
108
116
    def __init__(self):
109
117
        self._push_back_buffer = None
110
 
        
 
118
 
111
119
    def _push_back(self, bytes):
112
120
        """Return unused bytes to the medium, because they belong to the next
113
121
        request(s).
147
155
 
148
156
    def _get_line(self):
149
157
        """Read bytes from this request's response until a newline byte.
150
 
        
 
158
 
151
159
        This isn't particularly efficient, so should only be used when the
152
160
        expected size of the line is quite short.
153
161
 
156
164
        line, excess = _get_line(self.read_bytes)
157
165
        self._push_back(excess)
158
166
        return line
159
 
 
 
167
 
 
168
    def _report_activity(self, bytes, direction):
 
169
        """Notify that this medium has activity.
 
170
 
 
171
        Implementations should call this from all methods that actually do IO.
 
172
        Be careful that it's not called twice, if one method is implemented on
 
173
        top of another.
 
174
 
 
175
        :param bytes: Number of bytes read or written.
 
176
        :param direction: 'read' or 'write' or None.
 
177
        """
 
178
        ui.ui_factory.report_transport_activity(self, bytes, direction)
 
179
 
160
180
 
161
181
class SmartServerStreamMedium(SmartMedium):
162
182
    """Handles smart commands coming over a stream.
167
187
    One instance is created for each connected client; it can serve multiple
168
188
    requests in the lifetime of the connection.
169
189
 
170
 
    The server passes requests through to an underlying backing transport, 
 
190
    The server passes requests through to an underlying backing transport,
171
191
    which will typically be a LocalTransport looking at the server's filesystem.
172
192
 
173
193
    :ivar _push_back_buffer: a str of bytes that have been read from the stream
218
238
 
219
239
    def _serve_one_request(self, protocol):
220
240
        """Read one request from input, process, send back a response.
221
 
        
 
241
 
222
242
        :param protocol: a SmartServerRequestProtocol.
223
243
        """
224
244
        try:
263
283
                self.finished = True
264
284
                return
265
285
            protocol.accept_bytes(bytes)
266
 
        
 
286
 
267
287
        self._push_back(protocol.unused_data)
268
288
 
269
289
    def _read_bytes(self, desired_count):
270
 
        # We ignore the desired_count because on sockets it's more efficient to
271
 
        # read large chunks (of _MAX_READ_SIZE bytes) at a time.
272
 
        return self.socket.recv(_MAX_READ_SIZE)
 
290
        return _read_bytes_from_socket(
 
291
            self.socket.recv, desired_count, self._report_activity)
273
292
 
274
293
    def terminate_due_to_error(self):
275
294
        # TODO: This should log to a server log file, but no such thing
276
295
        # exists yet.  Andrew Bennetts 2006-09-29.
277
 
        self.socket.close()
 
296
        osutils.until_no_eintr(self.socket.close)
278
297
        self.finished = True
279
298
 
280
299
    def _write_out(self, bytes):
281
 
        osutils.send_all(self.socket, bytes)
 
300
        tstart = osutils.timer_func()
 
301
        osutils.send_all(self.socket, bytes, self._report_activity)
 
302
        if 'hpss' in debug.debug_flags:
 
303
            thread_id = thread.get_ident()
 
304
            trace.mutter('%12s: [%s] %d bytes to the socket in %.3fs'
 
305
                         % ('wrote', thread_id, len(bytes),
 
306
                            osutils.timer_func() - tstart))
282
307
 
283
308
 
284
309
class SmartServerPipeStreamMedium(SmartServerStreamMedium):
309
334
            bytes_to_read = protocol.next_read_size()
310
335
            if bytes_to_read == 0:
311
336
                # Finished serving this request.
312
 
                self._out.flush()
 
337
                osutils.until_no_eintr(self._out.flush)
313
338
                return
314
339
            bytes = self.read_bytes(bytes_to_read)
315
340
            if bytes == '':
316
341
                # Connection has been closed.
317
342
                self.finished = True
318
 
                self._out.flush()
 
343
                osutils.until_no_eintr(self._out.flush)
319
344
                return
320
345
            protocol.accept_bytes(bytes)
321
346
 
322
347
    def _read_bytes(self, desired_count):
323
 
        return self._in.read(desired_count)
 
348
        return osutils.until_no_eintr(self._in.read, desired_count)
324
349
 
325
350
    def terminate_due_to_error(self):
326
351
        # TODO: This should log to a server log file, but no such thing
327
352
        # exists yet.  Andrew Bennetts 2006-09-29.
328
 
        self._out.close()
 
353
        osutils.until_no_eintr(self._out.close)
329
354
        self.finished = True
330
355
 
331
356
    def _write_out(self, bytes):
332
 
        self._out.write(bytes)
 
357
        osutils.until_no_eintr(self._out.write, bytes)
333
358
 
334
359
 
335
360
class SmartClientMediumRequest(object):
345
370
    request.finished_reading()
346
371
 
347
372
    It is up to the individual SmartClientMedium whether multiple concurrent
348
 
    requests can exist. See SmartClientMedium.get_request to obtain instances 
349
 
    of SmartClientMediumRequest, and the concrete Medium you are using for 
 
373
    requests can exist. See SmartClientMedium.get_request to obtain instances
 
374
    of SmartClientMediumRequest, and the concrete Medium you are using for
350
375
    details on concurrency and pipelining.
351
376
    """
352
377
 
361
386
    def accept_bytes(self, bytes):
362
387
        """Accept bytes for inclusion in this request.
363
388
 
364
 
        This method may not be be called after finished_writing() has been
 
389
        This method may not be called after finished_writing() has been
365
390
        called.  It depends upon the Medium whether or not the bytes will be
366
391
        immediately transmitted. Message based Mediums will tend to buffer the
367
392
        bytes until finished_writing() is called.
398
423
    def _finished_reading(self):
399
424
        """Helper for finished_reading.
400
425
 
401
 
        finished_reading checks the state of the request to determine if 
 
426
        finished_reading checks the state of the request to determine if
402
427
        finished_reading is allowed, and if it is hands off to _finished_reading
403
428
        to perform the action.
404
429
        """
418
443
    def _finished_writing(self):
419
444
        """Helper for finished_writing.
420
445
 
421
 
        finished_writing checks the state of the request to determine if 
 
446
        finished_writing checks the state of the request to determine if
422
447
        finished_writing is allowed, and if it is hands off to _finished_writing
423
448
        to perform the action.
424
449
        """
444
469
        read_bytes checks the state of the request to determing if bytes
445
470
        should be read. After that it hands off to _read_bytes to do the
446
471
        actual read.
447
 
        
 
472
 
448
473
        By default this forwards to self._medium.read_bytes because we are
449
474
        operating on the medium's stream.
450
475
        """
455
480
        if not line.endswith('\n'):
456
481
            # end of file encountered reading from server
457
482
            raise errors.ConnectionReset(
458
 
                "please check connectivity and permissions",
459
 
                "(and try -Dhpss if further diagnosis is required)")
 
483
                "Unexpected end of message. Please check connectivity "
 
484
                "and permissions, and report a bug if problems persist.")
460
485
        return line
461
486
 
462
487
    def _read_line(self):
463
488
        """Helper for SmartClientMediumRequest.read_line.
464
 
        
 
489
 
465
490
        By default this forwards to self._medium._get_line because we are
466
491
        operating on the medium's stream.
467
492
        """
468
493
        return self._medium._get_line()
469
494
 
470
495
 
 
496
class _DebugCounter(object):
 
497
    """An object that counts the HPSS calls made to each client medium.
 
498
 
 
499
    When a medium is garbage-collected, or failing that when atexit functions
 
500
    are run, the total number of calls made on that medium are reported via
 
501
    trace.note.
 
502
    """
 
503
 
 
504
    def __init__(self):
 
505
        self.counts = weakref.WeakKeyDictionary()
 
506
        client._SmartClient.hooks.install_named_hook(
 
507
            'call', self.increment_call_count, 'hpss call counter')
 
508
        atexit.register(self.flush_all)
 
509
 
 
510
    def track(self, medium):
 
511
        """Start tracking calls made to a medium.
 
512
 
 
513
        This only keeps a weakref to the medium, so shouldn't affect the
 
514
        medium's lifetime.
 
515
        """
 
516
        medium_repr = repr(medium)
 
517
        # Add this medium to the WeakKeyDictionary
 
518
        self.counts[medium] = dict(count=0, vfs_count=0,
 
519
                                   medium_repr=medium_repr)
 
520
        # Weakref callbacks are fired in reverse order of their association
 
521
        # with the referenced object.  So we add a weakref *after* adding to
 
522
        # the WeakKeyDict so that we can report the value from it before the
 
523
        # entry is removed by the WeakKeyDict's own callback.
 
524
        ref = weakref.ref(medium, self.done)
 
525
 
 
526
    def increment_call_count(self, params):
 
527
        # Increment the count in the WeakKeyDictionary
 
528
        value = self.counts[params.medium]
 
529
        value['count'] += 1
 
530
        try:
 
531
            request_method = request.request_handlers.get(params.method)
 
532
        except KeyError:
 
533
            # A method we don't know about doesn't count as a VFS method.
 
534
            return
 
535
        if issubclass(request_method, vfs.VfsRequest):
 
536
            value['vfs_count'] += 1
 
537
 
 
538
    def done(self, ref):
 
539
        value = self.counts[ref]
 
540
        count, vfs_count, medium_repr = (
 
541
            value['count'], value['vfs_count'], value['medium_repr'])
 
542
        # In case this callback is invoked for the same ref twice (by the
 
543
        # weakref callback and by the atexit function), set the call count back
 
544
        # to 0 so this item won't be reported twice.
 
545
        value['count'] = 0
 
546
        value['vfs_count'] = 0
 
547
        if count != 0:
 
548
            trace.note('HPSS calls: %d (%d vfs) %s',
 
549
                       count, vfs_count, medium_repr)
 
550
 
 
551
    def flush_all(self):
 
552
        for ref in list(self.counts.keys()):
 
553
            self.done(ref)
 
554
 
 
555
_debug_counter = None
 
556
 
 
557
 
471
558
class SmartClientMedium(SmartMedium):
472
559
    """Smart client is a medium for sending smart protocol requests over."""
473
560
 
482
569
        # _remote_version_is_before tracks the bzr version the remote side
483
570
        # can be based on what we've seen so far.
484
571
        self._remote_version_is_before = None
 
572
        # Install debug hook function if debug flag is set.
 
573
        if 'hpss' in debug.debug_flags:
 
574
            global _debug_counter
 
575
            if _debug_counter is None:
 
576
                _debug_counter = _DebugCounter()
 
577
            _debug_counter.track(self)
485
578
 
486
579
    def _is_remote_before(self, version_tuple):
487
580
        """Is it possible the remote side supports RPCs for a given version?
512
605
        """
513
606
        if (self._remote_version_is_before is not None and
514
607
            version_tuple > self._remote_version_is_before):
 
608
            # We have been told that the remote side is older than some version
 
609
            # which is newer than a previously supplied older-than version.
 
610
            # This indicates that some smart verb call is not guarded
 
611
            # appropriately (it should simply not have been tried).
515
612
            raise AssertionError(
516
613
                "_remember_remote_is_before(%r) called, but "
517
614
                "_remember_remote_is_before(%r) was called previously."
555
652
 
556
653
    def disconnect(self):
557
654
        """If this medium maintains a persistent connection, close it.
558
 
        
 
655
 
559
656
        The default implementation does nothing.
560
657
        """
561
 
        
 
658
 
562
659
    def remote_path_from_transport(self, transport):
563
660
        """Convert transport into a path suitable for using in a request.
564
 
        
 
661
 
565
662
        Note that the resulting remote path doesn't encode the host name or
566
663
        anything but path, so it is only safe to use it in requests sent over
567
664
        the medium from the matching transport.
595
692
 
596
693
    def _flush(self):
597
694
        """Flush the output stream.
598
 
        
 
695
 
599
696
        This method is used by the SmartClientStreamMediumRequest to ensure that
600
697
        all data for a request is sent, to avoid long timeouts or deadlocks.
601
698
        """
612
709
 
613
710
class SmartSimplePipesClientMedium(SmartClientStreamMedium):
614
711
    """A client medium using simple pipes.
615
 
    
 
712
 
616
713
    This client does not manage the pipes: it assumes they will always be open.
617
714
    """
618
715
 
623
720
 
624
721
    def _accept_bytes(self, bytes):
625
722
        """See SmartClientStreamMedium.accept_bytes."""
626
 
        self._writeable_pipe.write(bytes)
 
723
        osutils.until_no_eintr(self._writeable_pipe.write, bytes)
 
724
        self._report_activity(len(bytes), 'write')
627
725
 
628
726
    def _flush(self):
629
727
        """See SmartClientStreamMedium._flush()."""
630
 
        self._writeable_pipe.flush()
 
728
        osutils.until_no_eintr(self._writeable_pipe.flush)
631
729
 
632
730
    def _read_bytes(self, count):
633
731
        """See SmartClientStreamMedium._read_bytes."""
634
 
        return self._readable_pipe.read(count)
 
732
        bytes = osutils.until_no_eintr(self._readable_pipe.read, count)
 
733
        self._report_activity(len(bytes), 'read')
 
734
        return bytes
635
735
 
636
736
 
637
737
class SmartSSHClientMedium(SmartClientStreamMedium):
638
738
    """A client medium using SSH."""
639
 
    
 
739
 
640
740
    def __init__(self, host, port=None, username=None, password=None,
641
741
            base=None, vendor=None, bzr_remote_path=None):
642
742
        """Creates a client that will connect on the first use.
643
 
        
 
743
 
644
744
        :param vendor: An optional override for the ssh vendor to use. See
645
745
            bzrlib.transport.ssh for details on ssh vendors.
646
746
        """
647
 
        SmartClientStreamMedium.__init__(self, base)
648
747
        self._connected = False
649
748
        self._host = host
650
749
        self._password = password
651
750
        self._port = port
652
751
        self._username = username
 
752
        # for the benefit of progress making a short description of this
 
753
        # transport
 
754
        self._scheme = 'bzr+ssh'
 
755
        # SmartClientStreamMedium stores the repr of this object in its
 
756
        # _DebugCounter so we have to store all the values used in our repr
 
757
        # method before calling the super init.
 
758
        SmartClientStreamMedium.__init__(self, base)
653
759
        self._read_from = None
654
760
        self._ssh_connection = None
655
761
        self._vendor = vendor
656
762
        self._write_to = None
657
763
        self._bzr_remote_path = bzr_remote_path
658
 
        if self._bzr_remote_path is None:
659
 
            symbol_versioning.warn(
660
 
                'bzr_remote_path is required as of bzr 0.92',
661
 
                DeprecationWarning, stacklevel=2)
662
 
            self._bzr_remote_path = os.environ.get('BZR_REMOTE_PATH', 'bzr')
 
764
 
 
765
    def __repr__(self):
 
766
        if self._port is None:
 
767
            maybe_port = ''
 
768
        else:
 
769
            maybe_port = ':%s' % self._port
 
770
        return "%s(%s://%s@%s%s/)" % (
 
771
            self.__class__.__name__,
 
772
            self._scheme,
 
773
            self._username,
 
774
            self._host,
 
775
            maybe_port)
663
776
 
664
777
    def _accept_bytes(self, bytes):
665
778
        """See SmartClientStreamMedium.accept_bytes."""
666
779
        self._ensure_connection()
667
 
        self._write_to.write(bytes)
 
780
        osutils.until_no_eintr(self._write_to.write, bytes)
 
781
        self._report_activity(len(bytes), 'write')
668
782
 
669
783
    def disconnect(self):
670
784
        """See SmartClientMedium.disconnect()."""
671
785
        if not self._connected:
672
786
            return
673
 
        self._read_from.close()
674
 
        self._write_to.close()
 
787
        osutils.until_no_eintr(self._read_from.close)
 
788
        osutils.until_no_eintr(self._write_to.close)
675
789
        self._ssh_connection.close()
676
790
        self._connected = False
677
791
 
700
814
        if not self._connected:
701
815
            raise errors.MediumNotConnected(self)
702
816
        bytes_to_read = min(count, _MAX_READ_SIZE)
703
 
        return self._read_from.read(bytes_to_read)
 
817
        bytes = osutils.until_no_eintr(self._read_from.read, bytes_to_read)
 
818
        self._report_activity(len(bytes), 'read')
 
819
        return bytes
704
820
 
705
821
 
706
822
# Port 4155 is the default port for bzr://, registered with IANA.
707
 
BZR_DEFAULT_INTERFACE = '0.0.0.0'
 
823
BZR_DEFAULT_INTERFACE = None
708
824
BZR_DEFAULT_PORT = 4155
709
825
 
710
826
 
711
827
class SmartTCPClientMedium(SmartClientStreamMedium):
712
828
    """A client medium using TCP."""
713
 
    
 
829
 
714
830
    def __init__(self, host, port, base):
715
831
        """Creates a client that will connect on the first use."""
716
832
        SmartClientStreamMedium.__init__(self, base)
722
838
    def _accept_bytes(self, bytes):
723
839
        """See SmartClientMedium.accept_bytes."""
724
840
        self._ensure_connection()
725
 
        osutils.send_all(self._socket, bytes)
 
841
        osutils.send_all(self._socket, bytes, self._report_activity)
726
842
 
727
843
    def disconnect(self):
728
844
        """See SmartClientMedium.disconnect()."""
729
845
        if not self._connected:
730
846
            return
731
 
        self._socket.close()
 
847
        osutils.until_no_eintr(self._socket.close)
732
848
        self._socket = None
733
849
        self._connected = False
734
850
 
736
852
        """Connect this medium if not already connected."""
737
853
        if self._connected:
738
854
            return
739
 
        self._socket = socket.socket()
740
 
        self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
741
855
        if self._port is None:
742
856
            port = BZR_DEFAULT_PORT
743
857
        else:
744
858
            port = int(self._port)
745
859
        try:
746
 
            self._socket.connect((self._host, port))
747
 
        except socket.error, err:
 
860
            sockaddrs = socket.getaddrinfo(self._host, port, socket.AF_UNSPEC,
 
861
                socket.SOCK_STREAM, 0, 0)
 
862
        except socket.gaierror, (err_num, err_msg):
 
863
            raise errors.ConnectionError("failed to lookup %s:%d: %s" %
 
864
                    (self._host, port, err_msg))
 
865
        # Initialize err in case there are no addresses returned:
 
866
        err = socket.error("no address found for %s" % self._host)
 
867
        for (family, socktype, proto, canonname, sockaddr) in sockaddrs:
 
868
            try:
 
869
                self._socket = socket.socket(family, socktype, proto)
 
870
                self._socket.setsockopt(socket.IPPROTO_TCP,
 
871
                                        socket.TCP_NODELAY, 1)
 
872
                self._socket.connect(sockaddr)
 
873
            except socket.error, err:
 
874
                if self._socket is not None:
 
875
                    self._socket.close()
 
876
                self._socket = None
 
877
                continue
 
878
            break
 
879
        if self._socket is None:
748
880
            # socket errors either have a (string) or (errno, string) as their
749
881
            # args.
750
882
            if type(err.args) is str:
757
889
 
758
890
    def _flush(self):
759
891
        """See SmartClientStreamMedium._flush().
760
 
        
761
 
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and 
 
892
 
 
893
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and
762
894
        add a means to do a flush, but that can be done in the future.
763
895
        """
764
896
 
766
898
        """See SmartClientMedium.read_bytes."""
767
899
        if not self._connected:
768
900
            raise errors.MediumNotConnected(self)
769
 
        # We ignore the desired_count because on sockets it's more efficient to
770
 
        # read large chunks (of _MAX_READ_SIZE bytes) at a time.
771
 
        return self._socket.recv(_MAX_READ_SIZE)
 
901
        return _read_bytes_from_socket(
 
902
            self._socket.recv, count, self._report_activity)
772
903
 
773
904
 
774
905
class SmartClientStreamMediumRequest(SmartClientMediumRequest):
787
918
 
788
919
    def _accept_bytes(self, bytes):
789
920
        """See SmartClientMediumRequest._accept_bytes.
790
 
        
 
921
 
791
922
        This forwards to self._medium._accept_bytes because we are operating
792
923
        on the mediums stream.
793
924
        """
796
927
    def _finished_reading(self):
797
928
        """See SmartClientMediumRequest._finished_reading.
798
929
 
799
 
        This clears the _current_request on self._medium to allow a new 
 
930
        This clears the _current_request on self._medium to allow a new
800
931
        request to be created.
801
932
        """
802
933
        if self._medium._current_request is not self:
803
934
            raise AssertionError()
804
935
        self._medium._current_request = None
805
 
        
 
936
 
806
937
    def _finished_writing(self):
807
938
        """See SmartClientMediumRequest._finished_writing.
808
939
 
810
941
        """
811
942
        self._medium._flush()
812
943
 
 
944
 
 
945
def _read_bytes_from_socket(sock, desired_count, report_activity):
 
946
    # We ignore the desired_count because on sockets it's more efficient to
 
947
    # read large chunks (of _MAX_READ_SIZE bytes) at a time.
 
948
    try:
 
949
        bytes = osutils.until_no_eintr(sock, _MAX_READ_SIZE)
 
950
    except socket.error, e:
 
951
        if len(e.args) and e.args[0] in (errno.ECONNRESET, 10054):
 
952
            # The connection was closed by the other side.  Callers expect an
 
953
            # empty string to signal end-of-stream.
 
954
            bytes = ''
 
955
        else:
 
956
            raise
 
957
    else:
 
958
        report_activity(len(bytes), 'read')
 
959
    return bytes
 
960