Ticket #5248: port-from-fd-5248-1.patch

File port-from-fd-5248-1.patch, 12.1 KB (added by rwall, 3 years ago)

Here is my first attempt...don't think Exarkun's going to like it :)

  • fake_inetd.py

    === added file 'fake_inetd.py'
     
     1#!/usr/bin/env python 
     2 
     3if __name__ == '__main__': 
     4    import fake_inetd 
     5    raise SystemExit(fake_inetd.main()) 
     6 
     7from select import select 
     8from socket import socket, SOL_SOCKET, SO_REUSEADDR 
     9from subprocess import Popen 
     10 
     11def main(): 
     12    s = socket() 
     13    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 
     14    s.bind(('127.0.0.1', 1080)) 
     15    s.listen(0) 
     16    while True: 
     17        r, w, e = select([s], [], []) 
     18        if r: 
     19            p = Popen(['python', 'line_reflector.py'], stdin=s.fileno()) 
     20            p.wait() 
  • inetd.conf

    === added file 'inetd.conf'
     
     11080 stream  tcp     wait  richard      /home/richard/Projects/Twisted/branches/port-from-fd-5248/line_reflector.py 
  • line_reflector.py

    === added file 'line_reflector.py'
     
     1#!/usr/bin/env python 
     2 
     3if __name__ == '__main__': 
     4    import line_reflector 
     5    raise SystemExit(line_reflector.main()) 
     6 
     7""" 
     8 
     9""" 
     10 
     11from twisted.internet import reactor 
     12from twisted.internet.tcp import SharedPort 
     13from twisted.internet.protocol import ServerFactory 
     14from twisted.protocols.basic import LineReceiver 
     15 
     16 
     17class LineReflector(LineReceiver): 
     18    def lineReceived(self, line): 
     19        msg = 'LINE: %r' % (line,) 
     20        print msg 
     21        self.sendLine(msg) 
     22        if line == 'QUIT': 
     23            self.transport.loseConnection() 
     24        if line == 'SHUTDOWN': 
     25            reactor.stop() 
     26 
     27 
     28def main(): 
     29    f = ServerFactory() 
     30    f.protocol = LineReflector 
     31    p = SharedPort(0, f, reactor=reactor) 
     32    p.startListening() 
     33 
     34    reactor.run() 
  • twisted/internet/base.py

    === modified file 'twisted/internet/base.py'
     
    10821082    addressFamily = None 
    10831083    socketType = None 
    10841084 
    1085     def createInternetSocket(self): 
    1086         s = socket.socket(self.addressFamily, self.socketType) 
     1085    def createInternetSocket(self, fd=None): 
     1086        """ 
     1087        @param fd: Optional filedescriptor number of an existing socket fd. 
     1088        """ 
     1089        if fd is None: 
     1090            s = socket.socket(self.addressFamily, self.socketType) 
     1091            fdesc._setCloseOnExec(s.fileno()) 
     1092        else: 
     1093            s = socket.fromfd(fd, self.addressFamily, self.socketType) 
     1094 
    10871095        s.setblocking(0) 
    1088         fdesc._setCloseOnExec(s.fileno()) 
     1096 
    10891097        return s 
    10901098 
    10911099 
  • twisted/internet/tcp.py

    === modified file 'twisted/internet/tcp.py'
     
    489489        return address.IPv4Address('TCP', *self.client) 
    490490 
    491491 
    492  
    493 class Port(base.BasePort, _SocketCloser): 
     492class BaseTcpPort(base.BasePort): 
    494493    """ 
    495494    A TCP server port, listening for connections. 
    496495 
     
    530529    # value when we are actually listening. 
    531530    _realPortNumber = None 
    532531 
    533     def __init__(self, port, factory, backlog=50, interface='', reactor=None): 
    534         """Initialize with a numeric port to listen on. 
    535         """ 
    536         base.BasePort.__init__(self, reactor=reactor) 
    537         self.port = port 
    538         self.factory = factory 
    539         self.backlog = backlog 
    540         self.interface = interface 
    541  
    542532    def __repr__(self): 
    543533        if self._realPortNumber is not None: 
    544534            return "<%s of %s on %s>" % (self.__class__, self.factory.__class__, 
     
    546536        else: 
    547537            return "<%s of %s (not listening)>" % (self.__class__, self.factory.__class__) 
    548538 
    549     def createInternetSocket(self): 
    550         s = base.BasePort.createInternetSocket(self) 
    551         if platformType == "posix" and sys.platform != "cygwin": 
    552             s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    553         return s 
    554  
    555539 
    556540    def startListening(self): 
    557541        """Create and bind my socket, and begin listening on it. 
     
    559543        This is called on unserialization, and must be called after creating a 
    560544        server to begin listening on the specified port. 
    561545        """ 
    562         try: 
    563             skt = self.createInternetSocket() 
    564             skt.bind((self.interface, self.port)) 
    565         except socket.error, le: 
    566             raise CannotListenError, (self.interface, self.port, le) 
     546        skt = self.createInternetSocket() 
    567547 
    568548        # Make sure that if we listened on port 0, we update that to 
    569549        # reflect what the OS actually assigned us. 
     
    658638            # and return, so handling it here works just as well. 
    659639            log.deferr() 
    660640 
     641 
    661642    def loseConnection(self, connDone=failure.Failure(main.CONNECTION_DONE)): 
    662643        """ 
    663644        Stop accepting connections on this port. 
     
    707688        """ 
    708689        return reflect.qual(self.factory.__class__) 
    709690 
     691 
    710692    def getHost(self): 
    711693        """Returns an IPv4Address. 
    712694 
     
    714696        """ 
    715697        return address.IPv4Address('TCP', *self.socket.getsockname()) 
    716698 
     699 
     700class Port(BaseTcpPort, _SocketCloser): 
     701    """ 
     702    A TCP server port, listening for connections. 
     703 
     704    When a connection is accepted, this will call a factory's buildProtocol 
     705    with the incoming address as an argument, according to the specification 
     706    described in L{twisted.internet.interfaces.IProtocolFactory}. 
     707 
     708    If you wish to change the sort of transport that will be used, the 
     709    C{transport} attribute will be called with the signature expected for 
     710    C{Server.__init__}, so it can be replaced. 
     711 
     712    @ivar deferred: a deferred created when L{stopListening} is called, and 
     713        that will fire when connection is lost. This is not to be used it 
     714        directly: prefer the deferred returned by L{stopListening} instead. 
     715    @type deferred: L{defer.Deferred} 
     716 
     717    @ivar disconnecting: flag indicating that the L{stopListening} method has 
     718        been called and that no connections should be accepted anymore. 
     719    @type disconnecting: C{bool} 
     720 
     721    @ivar connected: flag set once the listen has successfully been called on 
     722        the socket. 
     723    @type connected: C{bool} 
     724    """ 
     725 
     726    def __init__(self, port, factory, backlog=50, interface='', reactor=None): 
     727        """Initialize with a numeric port to listen on. 
     728        """ 
     729        BaseTcpPort.__init__(self, reactor=reactor) 
     730        self.port = port 
     731        self.factory = factory 
     732        self.backlog = backlog 
     733        self.interface = interface 
     734 
     735 
     736    def createInternetSocket(self): 
     737        try: 
     738            s = BaseTcpPort.createInternetSocket(self) 
     739            if platformType == "posix" and sys.platform != "cygwin": 
     740                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     741            s.bind((self.interface, self.port)) 
     742            return s 
     743        except socket.error, le: 
     744            raise CannotListenError, (self.interface, self.port, le) 
     745 
     746 
     747class SharedPort(BaseTcpPort): 
     748    """ 
     749    A TCP server port for which a listening socket already exists. eg 
     750    a socket may have been setup by a parent process such as inetd or 
     751    systemd. 
     752 
     753    The socket will not be destroyed when this Port stops listening or 
     754    is destroyed. 
     755    """ 
     756 
     757    def __init__(self, fd, factory, backlog=50, reactor=None): 
     758        """ 
     759        Initialize with the file descriptor number of an existing 
     760        listening socket. 
     761        """ 
     762        BaseTcpPort.__init__(self, reactor=reactor) 
     763        self.fd = fd 
     764        self.factory = factory 
     765        self.backlog = backlog 
     766 
     767 
     768    def createInternetSocket(self): 
     769        try: 
     770            s = BaseTcpPort.createInternetSocket(self, fd=self.fd) 
     771            if platformType == "posix" and sys.platform != "cygwin": 
     772                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     773            return s 
     774        except socket.error, le: 
     775            raise CannotListenError, (self.interface, self.fd, le) 
     776 
     777 
     778    def _closeSocket(self): 
     779        pass 
     780 
     781 
    717782class Connector(base.BaseConnector): 
    718783    def __init__(self, host, port, factory, timeout, bindAddress, reactor=None): 
    719784        self.host = host 
  • twisted/internet/test/test_unix.py

    === modified file 'twisted/internet/test/test_unix.py'
     
    159159        """ 
    160160        Get the expected connection lost message for a UNIX port 
    161161        """ 
    162         return "(UNIX Port %s Closed)" % (repr(port.port),) 
     162        return "(UNIX Port %s Closed)" % (repr(port.fileName),) 
    163163 
    164164 
    165165 
  • twisted/internet/unix.py

    === modified file 'twisted/internet/unix.py'
     
    6767 
    6868 
    6969 
    70 class Port(_UNIXPort, tcp.Port): 
     70class Port(_UNIXPort, tcp.BaseTcpPort): 
    7171    addressFamily = socket.AF_UNIX 
    7272    socketType = socket.SOCK_STREAM 
    7373 
     
    7575    lockFile = None 
    7676 
    7777    def __init__(self, fileName, factory, backlog=50, mode=0666, reactor=None, wantPID = 0): 
    78         tcp.Port.__init__(self, fileName, factory, backlog, reactor=reactor) 
     78        tcp.BaseTcpPort.__init__(self, reactor=reactor) 
     79        self.fileName = fileName 
     80        self.factory = factory 
     81        self.backlog = backlog 
     82 
    7983        self.mode = mode 
    8084        self.wantPID = wantPID 
    8185 
    8286    def __repr__(self): 
    8387        factoryName = reflect.qual(self.factory.__class__) 
    8488        if hasattr(self, 'socket'): 
    85             return '<%s on %r>' % (factoryName, self.port) 
     89            return '<%s on %r>' % (factoryName, self.fileName) 
    8690        else: 
    8791            return '<%s (not listening)>' % (factoryName,) 
    8892 
     
    96100        This is called on unserialization, and must be called after creating a 
    97101        server to begin listening on the specified port. 
    98102        """ 
    99         log.msg("%s starting on %r" % (self.factory.__class__, repr(self.port))) 
     103        log.msg("%s starting on %r" % (self.factory.__class__, repr(self.fileName))) 
    100104        if self.wantPID: 
    101             self.lockFile = lockfile.FilesystemLock(self.port + ".lock") 
     105            self.lockFile = lockfile.FilesystemLock(self.fileName + ".lock") 
    102106            if not self.lockFile.lock(): 
    103                 raise CannotListenError, (None, self.port, "Cannot acquire lock") 
     107                raise CannotListenError, (None, self.fileName, "Cannot acquire lock") 
    104108            else: 
    105109                if not self.lockFile.clean: 
    106110                    try: 
     
    109113                        # If it fails, there's not much else we can 
    110114                        # do.  The bind() below will fail with an 
    111115                        # exception that actually propagates. 
    112                         if stat.S_ISSOCK(os.stat(self.port).st_mode): 
    113                             os.remove(self.port) 
     116                        if stat.S_ISSOCK(os.stat(self.fileName).st_mode): 
     117                            os.remove(self.fileName) 
    114118                    except: 
    115119                        pass 
    116120 
    117121        self.factory.doStart() 
    118122        try: 
    119123            skt = self.createInternetSocket() 
    120             skt.bind(self.port) 
     124            skt.bind(self.fileName) 
    121125        except socket.error, le: 
    122             raise CannotListenError, (None, self.port, le) 
     126            raise CannotListenError, (None, self.fileName, le) 
    123127        else: 
    124             if _inFilesystemNamespace(self.port): 
     128            if _inFilesystemNamespace(self.fileName): 
    125129                # Make the socket readable and writable to the world. 
    126                 os.chmod(self.port, self.mode) 
     130                os.chmod(self.fileName, self.mode) 
    127131            skt.listen(self.backlog) 
    128132            self.connected = True 
    129133            self.socket = skt 
     
    136140        """ 
    137141        Log message for closing socket 
    138142        """ 
    139         log.msg('(UNIX Port %s Closed)' % (repr(self.port),)) 
    140  
    141  
    142     def connectionLost(self, reason): 
    143         if _inFilesystemNamespace(self.port): 
    144             os.unlink(self.port) 
     143        log.msg('(UNIX Port %s Closed)' % (repr(self.fileName),)) 
     144 
     145 
     146    def _closeSocket(self): 
     147        if _inFilesystemNamespace(self.fileName): 
     148            os.unlink(self.fileName) 
    145149        if self.lockFile is not None: 
    146150            self.lockFile.unlock() 
    147         tcp.Port.connectionLost(self, reason) 
    148151 
    149152 
    150153