~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/medium.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
bzrlib/transport/smart/__init__.py.
25
25
"""
26
26
 
27
 
from __future__ import absolute_import
28
 
 
29
27
import errno
30
28
import os
31
29
import sys
32
30
import time
 
31
import urllib
33
32
 
34
33
import bzrlib
35
34
from bzrlib.lazy_import import lazy_import
43
42
    debug,
44
43
    errors,
45
44
    trace,
46
 
    transport,
47
45
    ui,
48
46
    urlutils,
49
47
    )
232
230
        try:
233
231
            while not self.finished:
234
232
                server_protocol = self._build_protocol()
 
233
                # TODO: This seems inelegant:
 
234
                if server_protocol is None:
 
235
                    # We could 'continue' only to notice that self.finished is
 
236
                    # True...
 
237
                    break
235
238
                self._serve_one_request(server_protocol)
236
239
        except errors.ConnectionTimeout, e:
237
240
            trace.note('%s' % (e,))
323
326
 
324
327
        :param protocol: a SmartServerRequestProtocol.
325
328
        """
326
 
        if protocol is None:
327
 
            return
328
329
        try:
329
330
            self._serve_one_request_unguarded(protocol)
330
331
        except KeyboardInterrupt:
842
843
        """
843
844
        medium_base = urlutils.join(self.base, '/')
844
845
        rel_url = urlutils.relative_url(medium_base, transport.base)
845
 
        return urlutils.unquote(rel_url)
 
846
        return urllib.unquote(rel_url)
846
847
 
847
848
 
848
849
class SmartClientStreamMedium(SmartClientMedium):
883
884
        """
884
885
        return SmartClientStreamMediumRequest(self)
885
886
 
886
 
    def reset(self):
887
 
        """We have been disconnected, reset current state.
888
 
 
889
 
        This resets things like _current_request and connected state.
890
 
        """
891
 
        self.disconnect()
892
 
        self._current_request = None
893
 
 
894
887
 
895
888
class SmartSimplePipesClientMedium(SmartClientStreamMedium):
896
889
    """A client medium using simple pipes.
905
898
 
906
899
    def _accept_bytes(self, bytes):
907
900
        """See SmartClientStreamMedium.accept_bytes."""
908
 
        try:
909
 
            self._writeable_pipe.write(bytes)
910
 
        except IOError, e:
911
 
            if e.errno in (errno.EINVAL, errno.EPIPE):
912
 
                raise errors.ConnectionReset(
913
 
                    "Error trying to write to subprocess", e)
914
 
            raise
 
901
        self._writeable_pipe.write(bytes)
915
902
        self._report_activity(len(bytes), 'write')
916
903
 
917
904
    def _flush(self):
918
905
        """See SmartClientStreamMedium._flush()."""
919
 
        # Note: If flush were to fail, we'd like to raise ConnectionReset, etc.
920
 
        #       However, testing shows that even when the child process is
921
 
        #       gone, this doesn't error.
922
906
        self._writeable_pipe.flush()
923
907
 
924
908
    def _read_bytes(self, count):
943
927
 
944
928
class SmartSSHClientMedium(SmartClientStreamMedium):
945
929
    """A client medium using SSH.
946
 
 
947
 
    It delegates IO to a SmartSimplePipesClientMedium or
 
930
    
 
931
    It delegates IO to a SmartClientSocketMedium or
948
932
    SmartClientAlreadyConnectedSocketMedium (depending on platform).
949
933
    """
950
934
 
972
956
            maybe_port = ''
973
957
        else:
974
958
            maybe_port = ':%s' % self._ssh_params.port
975
 
        if self._ssh_params.username is None:
976
 
            maybe_user = ''
977
 
        else:
978
 
            maybe_user = '%s@' % self._ssh_params.username
979
 
        return "%s(%s://%s%s%s/)" % (
 
959
        return "%s(%s://%s@%s%s/)" % (
980
960
            self.__class__.__name__,
981
961
            self._scheme,
982
 
            maybe_user,
 
962
            self._ssh_params.username,
983
963
            self._ssh_params.host,
984
964
            maybe_port)
985
965
 
1022
1002
            raise AssertionError(
1023
1003
                "Unexpected io_kind %r from %r"
1024
1004
                % (io_kind, self._ssh_connection))
1025
 
        for hook in transport.Transport.hooks["post_connect"]:
1026
 
            hook(self)
1027
1005
 
1028
1006
    def _flush(self):
1029
1007
        """See SmartClientStreamMedium._flush()."""
1043
1021
 
1044
1022
class SmartClientSocketMedium(SmartClientStreamMedium):
1045
1023
    """A client medium using a socket.
1046
 
 
 
1024
    
1047
1025
    This class isn't usable directly.  Use one of its subclasses instead.
1048
1026
    """
1049
1027
 
1132
1110
            raise errors.ConnectionError("failed to connect to %s:%d: %s" %
1133
1111
                    (self._host, port, err_msg))
1134
1112
        self._connected = True
1135
 
        for hook in transport.Transport.hooks["post_connect"]:
1136
 
            hook(self)
1137
1113
 
1138
1114
 
1139
1115
class SmartClientAlreadyConnectedSocketMedium(SmartClientSocketMedium):
1191
1167
        This invokes self._medium._flush to ensure all bytes are transmitted.
1192
1168
        """
1193
1169
        self._medium._flush()
 
1170
 
 
1171