~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to sandbox/exarkun/tcp.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2004-06-21 22:01:11 UTC
  • mto: (2.2.3 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040621220111-vkf909euqnyrp3nr
Tags: upstream-1.3.0
ImportĀ upstreamĀ versionĀ 1.3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_tcp -*-
 
2
# Twisted, the Framework of Your Internet
 
3
# Copyright (C) 2001 Matthew W. Lefkowitz
 
4
#
 
5
# This library is free software; you can redistribute it and/or
 
6
# modify it under the terms of version 2.1 of the GNU Lesser General Public
 
7
# License as published by the Free Software Foundation.
 
8
#
 
9
# This library is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
# Lesser General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU Lesser General Public
 
15
# License along with this library; if not, write to the Free Software
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
 
 
18
 
 
19
"""Various asynchronous TCP/IP classes.
 
20
 
 
21
End users shouldn't use this module directly - use the reactor APIs instead.
 
22
 
 
23
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
 
24
"""
 
25
 
 
26
 
 
27
# System Imports
 
28
import os
 
29
import stat
 
30
import types
 
31
import exceptions
 
32
import socket
 
33
import sys
 
34
import select
 
35
import operator
 
36
import warnings
 
37
try:
 
38
    import fcntl
 
39
except ImportError:
 
40
    fcntl = None
 
41
 
 
42
try:
 
43
    from OpenSSL import SSL
 
44
except ImportError:
 
45
    SSL = None
 
46
 
 
47
if os.name == 'nt':
 
48
    # we hardcode these since windows actually wants e.g.
 
49
    # WSAEALREADY rather than EALREADY. Possibly we should
 
50
    # just be doing "from errno import WSAEALREADY as EALREADY".
 
51
    EPERM       = 10001
 
52
    EINVAL      = 10022
 
53
    EWOULDBLOCK = 10035
 
54
    EINPROGRESS = 10036
 
55
    EALREADY    = 10037
 
56
    ECONNRESET  = 10054
 
57
    EISCONN     = 10056
 
58
    ENOTCONN    = 10057
 
59
    EINTR       = 10004
 
60
elif os.name != 'java':
 
61
    from errno import EPERM
 
62
    from errno import EINVAL
 
63
    from errno import EWOULDBLOCK
 
64
    from errno import EINPROGRESS
 
65
    from errno import EALREADY
 
66
    from errno import ECONNRESET
 
67
    from errno import EISCONN
 
68
    from errno import ENOTCONN
 
69
    from errno import EINTR
 
70
from errno import EAGAIN
 
71
 
 
72
# Twisted Imports
 
73
from twisted.internet import protocol, defer, base
 
74
from twisted.persisted import styles
 
75
from twisted.python import log, failure, reflect
 
76
from twisted.python.runtime import platform, platformType
 
77
from twisted.internet.error import CannotListenError
 
78
 
 
79
# Sibling Imports
 
80
import abstract
 
81
import main
 
82
import interfaces
 
83
import error
 
84
 
 
85
class _TLSMixin:
 
86
    writeBlockedOnRead = 0
 
87
    readBlockedOnWrite = 0
 
88
    sslShutdown = 0
 
89
 
 
90
    def getPeerCertificate(self):
 
91
        return self.socket.get_peer_certificate()
 
92
    
 
93
    def doRead(self):
 
94
        if self.writeBlockedOnRead:
 
95
            self.writeBlockedOnRead = 0
 
96
            self.startWriting()
 
97
        try:
 
98
            return Connection.doRead(self)
 
99
        except SSL.ZeroReturnError:
 
100
            # close SSL layer, since other side has done so, if we haven't
 
101
            if not self.sslShutdown:
 
102
                try:
 
103
                    self.socket.shutdown()
 
104
                    self.sslShutdown = 1
 
105
                except SSL.Error:
 
106
                    pass
 
107
            print 'losting conn1'
 
108
            return main.CONNECTION_DONE
 
109
        except SSL.WantReadError:
 
110
            return
 
111
        except SSL.WantWriteError:
 
112
            self.readBlockedOnWrite = 1
 
113
            self.startWriting()
 
114
            return
 
115
        except SSL.Error:
 
116
            print 'losting conn2'
 
117
            log.err()
 
118
            return main.CONNECTION_LOST
 
119
 
 
120
    def loseConnection(self):
 
121
        Connection.loseConnection(self)
 
122
        if self.connected:
 
123
            self.startReading()
 
124
    
 
125
    def doWrite(self):
 
126
        if self.writeBlockedOnRead:
 
127
            self.stopWriting()
 
128
            return
 
129
        if self.readBlockedOnWrite:
 
130
            self.readBlockedOnWrite = 0
 
131
            # XXX - This is touching internal guts bad bad bad
 
132
            if not self.dataBuffer:
 
133
                self.stopWriting()
 
134
            return self.doRead()
 
135
        return Connection.doWrite(self)
 
136
 
 
137
    def writeSomeData(self, data):
 
138
        try:
 
139
            return _BufferFlushBase.writeSomeData(self, data)
 
140
        except SSL.WantWriteError:
 
141
            return 0
 
142
        except SSL.WantReadError:
 
143
            self.writeBlockedOnRead = 1
 
144
            return 0
 
145
        except SSL.SysCallError, e:
 
146
            if e[0] == -1 and data == "":
 
147
                # errors when writing empty strings are expected
 
148
                # and can be ignored
 
149
                return 0
 
150
            else:
 
151
                print 'losting conn3'
 
152
                return main.CONNECTION_LOST
 
153
        except SSL.Error:
 
154
            print 'losting conn4'
 
155
            log.err()
 
156
            return main.CONNECTION_LOST
 
157
        
 
158
 
 
159
    def write(self, data):
 
160
        """Reliably write some data.
 
161
 
 
162
        If there is no buffered data this tries to write this data immediately,
 
163
        otherwise this adds data to be written the next time this file descriptor is
 
164
        ready for writing.
 
165
        """
 
166
        assert isinstance(data, str), "Data must be a string."
 
167
        if not self.connected or not data:
 
168
            return
 
169
        if (not self.dataBuffer) and (self.producer is None):
 
170
            l = self.writeSomeData(data)
 
171
            if l == len(data):
 
172
                # all data was sent, our work here is done
 
173
                return
 
174
            elif not isinstance(l, Exception) and l > 0:
 
175
                # some data was sent
 
176
                self.dataBuffer = data
 
177
                self.offset = l
 
178
            else:
 
179
                # either no data was sent, or we were disconnected.
 
180
                # if we were disconnected we still continue, so that
 
181
                # the event loop can figure it out later on.
 
182
                self.dataBuffer = data
 
183
        else:
 
184
            self.dataBuffer = self.dataBuffer + data
 
185
        if self.producer is not None:
 
186
            if len(self.dataBuffer) > self.bufferSize:
 
187
                self.producerPaused = 1
 
188
                self.producer.pauseProducing()
 
189
        self.startWriting()
 
190
 
 
191
    def writeSequence(self, iovec):
 
192
        self.write("".join(iovec))
 
193
 
 
194
    def _closeSocket(self):
 
195
        try:
 
196
            self.socket.sock_shutdown(2)
 
197
        except:
 
198
            pass
 
199
        try:
 
200
            self.socket.close()
 
201
        except:
 
202
            pass
 
203
 
 
204
    def _postLoseConnection(self):
 
205
        """Gets called after loseConnection(), after buffered data is sent.
 
206
 
 
207
        We close the SSL transport layer, and if the other side hasn't
 
208
        closed it yet we start reading, waiting for a ZeroReturnError
 
209
        which will indicate the SSL shutdown has completed.
 
210
        """
 
211
        try:
 
212
            done = self.socket.shutdown()
 
213
            self.sslShutdown = 1
 
214
        except SSL.Error:
 
215
            log.err()
 
216
            return main.CONNECTION_LOST
 
217
        if done:
 
218
            return main.CONNECTION_DONE
 
219
        else:
 
220
            # we wait for other side to close SSL connection -
 
221
            # this will be signaled by SSL.ZeroReturnError when reading
 
222
            # from the socket
 
223
            self.stopWriting()
 
224
            self.startReading()
 
225
            
 
226
            # don't close socket just yet
 
227
            return None
 
228
 
 
229
class _BufferFlushBase(abstract.FileDescriptor):
 
230
    def writeSomeData(self, data):
 
231
        """Connection.writeSomeData(data) -> #of bytes written | CONNECTION_LOST
 
232
        This writes as much data as possible to the socket and returns either
 
233
        the number of bytes read (which is positive) or a connection error code
 
234
        (which is negative)
 
235
        """
 
236
        try:
 
237
            return self.socket.send(data)
 
238
        except socket.error, se:
 
239
            if se.args[0] == EINTR:
 
240
                return self.writeSomeData(data)
 
241
            elif se.args[0] == EWOULDBLOCK:
 
242
                return 0
 
243
            else:
 
244
                return main.CONNECTION_LOST
 
245
 
 
246
    def _flattenForSSL(self):
 
247
        pass
 
248
 
 
249
class _IOVecFlushBase(abstract.FileDescriptor):
 
250
    def _flattenForSSL(self):
 
251
        self.dataBuffer = ''.join(self.vector)
 
252
        self.offset = 0
 
253
        del self.vector
 
254
 
 
255
    def writeVector(self, vector):
 
256
        written, errno = iovec.writev(self.fileno(), vector)
 
257
        if written == -1:
 
258
            if errno == EINTR:
 
259
                return self.writeVector(vector)
 
260
            elif errno == EWOULDBLOCK:
 
261
                return 0
 
262
            else:
 
263
                log.msg("writev() failed with errno = %d" % (errno,))
 
264
                return main.CONNECTION_LOST
 
265
 
 
266
        w = written
 
267
        i = 0
 
268
        L = len(vector)
 
269
        while i < L and w >= len(vector[i]):
 
270
            w -= len(vector[i])
 
271
            i += 1
 
272
        del vector[:i]
 
273
        if w:
 
274
            vector[0] = vector[0][w:]
 
275
        return written
 
276
 
 
277
try:
 
278
    from twisted.python import iovec
 
279
except ImportError:
 
280
    _FlushBase = _BufferFlushBase
 
281
else:
 
282
    _FlushBase = _IOVecFlushBase
 
283
    
 
284
class Connection(_FlushBase):
 
285
    """I am the superclass of all socket-based FileDescriptors.
 
286
 
 
287
    This is an abstract superclass of all objects which represent a TCP/IP
 
288
    connection based socket.
 
289
    """
 
290
 
 
291
    __implements__ = abstract.FileDescriptor.__implements__, interfaces.ITCPTransport
 
292
 
 
293
    TLS = 0
 
294
 
 
295
    def __init__(self, skt, protocol, reactor=None):
 
296
        abstract.FileDescriptor.__init__(self, reactor=reactor)
 
297
        self.socket = skt
 
298
        self.socket.setblocking(0)
 
299
        self.fileno = skt.fileno
 
300
        self.protocol = protocol
 
301
 
 
302
    if SSL:
 
303
        __implements__ = __implements__ + (interfaces.ITLSTransport,)
 
304
 
 
305
        def startTLS(self, ctx):
 
306
            assert not self.TLS
 
307
            self.stopReading()
 
308
            self.stopWriting()
 
309
            self._startTLS()
 
310
            self.socket = SSL.Connection(ctx.getContext(), self.socket)
 
311
            self.fileno = self.socket.fileno
 
312
            self.startReading()
 
313
 
 
314
        def _startTLS(self):
 
315
            self.TLS = 1
 
316
            class TLSConnection(_TLSMixin, _BufferFlushBase, self.__class__):
 
317
                pass
 
318
            self._flattenForSSL()
 
319
            self.__class__ = TLSConnection
 
320
 
 
321
    def doRead(self):
 
322
        """Calls self.protocol.dataReceived with all available data.
 
323
 
 
324
        This reads up to self.bufferSize bytes of data from its socket, then
 
325
        calls self.dataReceived(data) to process it.  If the connection is not
 
326
        lost through an error in the physical recv(), this function will return
 
327
        the result of the dataReceived call.
 
328
        """
 
329
        try:
 
330
            data = self.socket.recv(self.bufferSize)
 
331
        except socket.error, se:
 
332
            if se.args[0] == EWOULDBLOCK:
 
333
                return
 
334
            else:
 
335
                return main.CONNECTION_LOST
 
336
        except SSL.SysCallError, (retval, desc):
 
337
            # Yes, SSL might be None, but self.socket.recv() can *only*
 
338
            # raise socket.error, if anything else is raised, it must be an
 
339
            # SSL socket, and so SSL can't be None. (That's my story, I'm
 
340
            # stickin' to it)
 
341
            if retval == -1 and desc == 'Unexpected EOF':
 
342
                return main.CONNECTION_DONE
 
343
            raise
 
344
        if not data:
 
345
            return main.CONNECTION_DONE
 
346
        return self.protocol.dataReceived(data)
 
347
 
 
348
 
 
349
    def _closeSocket(self):
 
350
        """Called to close our socket."""
 
351
        # This used to close() the socket, but that doesn't *really* close if
 
352
        # there's another reference to it in the TCP/IP stack, e.g. if it was
 
353
        # was inherited by a subprocess. And we really do want to close the
 
354
        # connection. So we use shutdown() instead.
 
355
        try:
 
356
            self.socket.shutdown(2)
 
357
        except socket.error:
 
358
            pass
 
359
 
 
360
    def connectionLost(self, reason):
 
361
        """See abstract.FileDescriptor.connectionLost().
 
362
        """
 
363
        abstract.FileDescriptor.connectionLost(self, reason)
 
364
        self._closeSocket()
 
365
        protocol = self.protocol
 
366
        del self.protocol
 
367
        del self.socket
 
368
        del self.fileno
 
369
        try:
 
370
            protocol.connectionLost(reason)
 
371
        except TypeError, e:
 
372
            # while this may break, it will only break on deprecated code
 
373
            # as opposed to other approaches that might've broken on
 
374
            # code that uses the new API (e.g. inspect).
 
375
            if e.args and e.args[0] == "connectionLost() takes exactly 1 argument (2 given)":
 
376
                warnings.warn("Protocol %s's connectionLost should accept a reason argument" % protocol,
 
377
                              category=DeprecationWarning, stacklevel=2)
 
378
                protocol.connectionLost()
 
379
            else:
 
380
                raise
 
381
 
 
382
    logstr = "Uninitialized"
 
383
 
 
384
    def logPrefix(self):
 
385
        """Return the prefix to log with when I own the logging thread.
 
386
        """
 
387
        return self.logstr
 
388
 
 
389
    def getTcpNoDelay(self):
 
390
        return operator.truth(self.socket.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY))
 
391
 
 
392
    def setTcpNoDelay(self, enabled):
 
393
        self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, enabled)
 
394
 
 
395
    def getTcpKeepAlive(self):
 
396
        return operator.truth(self.socket.getsockopt(socket.SOL_SOCKET,
 
397
                                                     socket.SO_KEEPALIVE))
 
398
 
 
399
    def setTcpKeepAlive(self, enabled):
 
400
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, enabled)
 
401
 
 
402
 
 
403
class BaseClient(Connection):
 
404
    """A base class for client TCP (and similiar) sockets.
 
405
    """
 
406
    addressFamily = socket.AF_INET
 
407
    socketType = socket.SOCK_STREAM
 
408
 
 
409
    def _finishInit(self, whenDone, skt, error, reactor):
 
410
        """Called by base classes to continue to next stage of initialization."""
 
411
        if whenDone:
 
412
            Connection.__init__(self, skt, None, reactor)
 
413
            self.doWrite = self.doConnect
 
414
            self.doRead = self.doConnect
 
415
            reactor.callLater(0, whenDone)
 
416
        else:
 
417
            reactor.callLater(0, self.failIfNotConnected, error)
 
418
 
 
419
    def startTLS(self, ctx, client=1):
 
420
        holder = Connection.startTLS(self, ctx)
 
421
        if client:
 
422
            self.socket.set_connect_state()
 
423
        else:
 
424
            self.socket.set_accept_state()
 
425
        return holder
 
426
 
 
427
    def stopConnecting(self):
 
428
        """Stop attempt to connect."""
 
429
        self.failIfNotConnected(error.UserError())
 
430
 
 
431
    def failIfNotConnected(self, err):
 
432
        if (self.connected or
 
433
            self.disconnected or
 
434
            not (hasattr(self, "connector"))):
 
435
            return
 
436
        self.connector.connectionFailed(failure.Failure(err))
 
437
        if hasattr(self, "reactor"):
 
438
            # this doesn't happens if we failed in __init__
 
439
            self.stopReading()
 
440
            self.stopWriting()
 
441
            del self.connector
 
442
 
 
443
    def createInternetSocket(self):
 
444
        """(internal) Create a non-blocking socket using
 
445
        self.addressFamily, self.socketType.
 
446
        """
 
447
        s = socket.socket(self.addressFamily, self.socketType)
 
448
        s.setblocking(0)
 
449
        if fcntl and hasattr(fcntl, 'FD_CLOEXEC'):
 
450
            old = fcntl.fcntl(s.fileno(), fcntl.F_GETFD)
 
451
            fcntl.fcntl(s.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
 
452
        return s
 
453
 
 
454
 
 
455
    def resolveAddress(self):
 
456
        if abstract.isIPAddress(self.addr[0]):
 
457
            self._setRealAddress(self.addr[0])
 
458
        else:
 
459
            d = self.reactor.resolve(self.addr[0])
 
460
            d.addCallbacks(self._setRealAddress, self.failIfNotConnected)
 
461
 
 
462
    def _setRealAddress(self, address):
 
463
        self.realAddress = (address, self.addr[1])
 
464
        self.doConnect()
 
465
 
 
466
    def doConnect(self):
 
467
        """I connect the socket.
 
468
 
 
469
        Then, call the protocol's makeConnection, and start waiting for data.
 
470
        """
 
471
        if not hasattr(self, "connector"):
 
472
            # this happens when connection failed but doConnect
 
473
            # was scheduled via a callLater in self._finishInit
 
474
            return
 
475
 
 
476
        # on windows failed connects are reported on exception
 
477
        # list, not write or read list.
 
478
        if platformType == "win32":
 
479
            r, w, e = select.select([], [], [self.fileno()], 0.0)
 
480
            if e:
 
481
                err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
 
482
                self.failIfNotConnected(error.getConnectError((err, os.strerror(err))))
 
483
                return
 
484
 
 
485
        try:
 
486
            connectResult = self.socket.connect_ex(self.realAddress)
 
487
        except socket.error, se:
 
488
            connectResult = se.args[0]
 
489
        if connectResult:
 
490
            if connectResult == EISCONN:
 
491
                pass
 
492
            # on Windows EINVAL means sometimes that we should keep trying:
 
493
            # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/connect_2.asp
 
494
            elif ((connectResult in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or
 
495
                  (connectResult == EINVAL and platformType == "win32")):
 
496
                self.startReading()
 
497
                self.startWriting()
 
498
                return
 
499
            else:
 
500
                self.failIfNotConnected(error.getConnectError((connectResult, os.strerror(connectResult))))
 
501
                return
 
502
        # If I have reached this point without raising or returning, that means
 
503
        # that the socket is connected.
 
504
        del self.doWrite
 
505
        del self.doRead
 
506
        # we first stop and then start, to reset any references to the old doRead
 
507
        self.stopReading()
 
508
        self.stopWriting()
 
509
        self._connectDone()
 
510
 
 
511
    def _connectDone(self):
 
512
        self.protocol = self.connector.buildProtocol(self.getPeer())
 
513
        self.connected = 1
 
514
        self.protocol.makeConnection(self)
 
515
        self.logstr = self.protocol.__class__.__name__+",client"
 
516
        self.startReading()
 
517
 
 
518
    def connectionLost(self, reason):
 
519
        if not self.connected:
 
520
            self.failIfNotConnected(error.ConnectError(string=reason))
 
521
        else:
 
522
            Connection.connectionLost(self, reason)
 
523
            self.connector.connectionLost(reason)
 
524
 
 
525
 
 
526
class Client(BaseClient):
 
527
    """A TCP client."""
 
528
 
 
529
    def __init__(self, host, port, bindAddress, connector, reactor=None):
 
530
        # BaseClient.__init__ is invoked later
 
531
        self.connector = connector
 
532
        self.addr = (host, port)
 
533
        
 
534
        whenDone = self.resolveAddress
 
535
        err = None
 
536
        skt = None
 
537
 
 
538
        try:
 
539
            skt = self.createInternetSocket()
 
540
        except socket.error, se:
 
541
            err = error.ConnectBindError(se[0], se[1])
 
542
            whenDone = None
 
543
        if whenDone and bindAddress is not None:
 
544
            try:
 
545
                skt.bind(bindAddress)
 
546
            except socket.error, se:
 
547
                err = error.ConnectBindError(se[0], se[1])
 
548
                whenDone = None
 
549
        self._finishInit(whenDone, skt, err, reactor)
 
550
 
 
551
    def getHost(self):
 
552
        """Returns a tuple of ('INET', hostname, port).
 
553
 
 
554
        This indicates the address from which I am connecting.
 
555
        """
 
556
        return ('INET',)+self.socket.getsockname()
 
557
 
 
558
    def getPeer(self):
 
559
        """Returns a tuple of ('INET', hostname, port).
 
560
 
 
561
        This indicates the address that I am connected to.
 
562
        """
 
563
        return ('INET',)+self.addr
 
564
 
 
565
    def __repr__(self):
 
566
        s = '<%s to %s at %x>' % (self.__class__, self.addr, id(self))
 
567
        return s
 
568
 
 
569
 
 
570
class Server(Connection):
 
571
    """Serverside socket-stream connection class.
 
572
 
 
573
    I am a serverside network connection transport; a socket which came from an
 
574
    accept() on a server.
 
575
    """
 
576
 
 
577
    def __init__(self, sock, protocol, client, server, sessionno):
 
578
        """Server(sock, protocol, client, server, sessionno)
 
579
 
 
580
        Initialize me with a socket, a protocol, a descriptor for my peer (a
 
581
        tuple of host, port describing the other end of the connection), an
 
582
        instance of Port, and a session number.
 
583
        """
 
584
        Connection.__init__(self, sock, protocol)
 
585
        self.server = server
 
586
        self.client = client
 
587
        self.sessionno = sessionno
 
588
        self.hostname = client[0]
 
589
        self.logstr = "%s,%s,%s" % (self.protocol.__class__.__name__, sessionno, self.hostname)
 
590
        self.repstr = "<%s #%s on %s>" % (self.protocol.__class__.__name__, self.sessionno, self.server.port)
 
591
        self.startReading()
 
592
        self.connected = 1
 
593
 
 
594
    def __repr__(self):
 
595
        """A string representation of this connection.
 
596
        """
 
597
        return self.repstr
 
598
 
 
599
    def startTLS(self, ctx, server=1):
 
600
        holder = Connection.startTLS(self, ctx)
 
601
        if server:
 
602
            self.socket.set_accept_state()
 
603
        else:
 
604
            self.socket.set_connect_state()
 
605
        return holder
 
606
 
 
607
    def getHost(self):
 
608
        """Returns a tuple of ('INET', hostname, port).
 
609
 
 
610
        This indicates the servers address.
 
611
        """
 
612
        return ('INET',)+self.socket.getsockname()
 
613
 
 
614
    def getPeer(self):
 
615
        """
 
616
        Returns a tuple of ('INET', hostname, port), indicating the connected
 
617
        client's address.
 
618
        """
 
619
        return ('INET',)+self.client
 
620
 
 
621
 
 
622
class Port(base.BasePort):
 
623
    """I am a TCP server port, listening for connections.
 
624
 
 
625
    When a connection is accepted, I will call my factory's buildProtocol with
 
626
    the incoming connection as an argument, according to the specification
 
627
    described in twisted.internet.interfaces.IProtocolFactory.
 
628
 
 
629
    If you wish to change the sort of transport that will be used, my
 
630
    `transport' attribute will be called with the signature expected for
 
631
    Server.__init__, so it can be replaced.
 
632
    """
 
633
    addressFamily = socket.AF_INET
 
634
    socketType = socket.SOCK_STREAM
 
635
 
 
636
    transport = Server
 
637
    sessionno = 0
 
638
    interface = ''
 
639
    backlog = 5
 
640
 
 
641
    def __init__(self, port, factory, backlog=5, interface='', reactor=None):
 
642
        """Initialize with a numeric port to listen on.
 
643
        """
 
644
        base.BasePort.__init__(self, reactor=reactor)
 
645
        self.port = port
 
646
        self.factory = factory
 
647
        self.backlog = backlog
 
648
        self.interface = interface
 
649
 
 
650
    def __repr__(self):
 
651
        return "<%s on %s>" % (self.factory.__class__, self.port)
 
652
 
 
653
    def createInternetSocket(self):
 
654
        s = base.BasePort.createInternetSocket(self)
 
655
        if platformType == "posix":
 
656
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
657
        return s
 
658
 
 
659
    def startListening(self):
 
660
        """Create and bind my socket, and begin listening on it.
 
661
 
 
662
        This is called on unserialization, and must be called after creating a
 
663
        server to begin listening on the specified port.
 
664
        """
 
665
        log.msg("%s starting on %s"%(self.factory.__class__, self.port))
 
666
        try:
 
667
            skt = self.createInternetSocket()
 
668
            skt.bind((self.interface, self.port))
 
669
        except socket.error, le:
 
670
            raise CannotListenError, (self.interface, self.port, le)
 
671
        self.factory.doStart()
 
672
        skt.listen(self.backlog)
 
673
        self.connected = 1
 
674
        self.socket = skt
 
675
        self.fileno = self.socket.fileno
 
676
        self.numberAccepts = 100
 
677
        self.startReading()
 
678
 
 
679
    def doRead(self):
 
680
        """Called when my socket is ready for reading.
 
681
 
 
682
        This accepts a connection and calls self.protocol() to handle the
 
683
        wire-level protocol.
 
684
        """
 
685
        try:
 
686
            if platformType == "posix":
 
687
                numAccepts = self.numberAccepts
 
688
            else:
 
689
                # win32 event loop breaks if we do more than one accept()
 
690
                # in an iteration of the event loop.
 
691
                numAccepts = 1
 
692
            for i in range(numAccepts):
 
693
                # we need this so we can deal with a factory's buildProtocol
 
694
                # calling our loseConnection
 
695
                if self.disconnecting:
 
696
                    return
 
697
                try:
 
698
                    skt, addr = self.socket.accept()
 
699
                except socket.error, e:
 
700
                    if e.args[0] in (EWOULDBLOCK, EAGAIN):
 
701
                        self.numberAccepts = i
 
702
                        break
 
703
                    elif e.args[0] == EPERM:
 
704
                        continue
 
705
                    raise
 
706
                
 
707
                protocol = self.factory.buildProtocol(addr)
 
708
                if protocol is None:
 
709
                    skt.close()
 
710
                    continue
 
711
                s = self.sessionno
 
712
                self.sessionno = s+1
 
713
                transport = self.transport(skt, protocol, addr, self, s)
 
714
                transport = self._preMakeConnection(transport)
 
715
                protocol.makeConnection(transport)
 
716
            else:
 
717
                self.numberAccepts = self.numberAccepts+20
 
718
        except:
 
719
            # Note that in TLS mode, this will possibly catch SSL.Errors
 
720
            # raised by self.socket.accept()
 
721
            #
 
722
            # There is no "except SSL.Error:" above because SSL may be
 
723
            # None if there is no SSL support.  In any case, all the
 
724
            # "except SSL.Error:" suite would probably do is log.deferr()
 
725
            # and return, so handling it here works just as well.
 
726
            log.deferr()
 
727
 
 
728
    def _preMakeConnection(self, transport):
 
729
        return transport
 
730
 
 
731
    def loseConnection(self, connDone=failure.Failure(main.CONNECTION_DONE)):
 
732
        """Stop accepting connections on this port.
 
733
 
 
734
        This will shut down my socket and call self.connectionLost().
 
735
        It returns a deferred which will fire successfully when the
 
736
        port is actually closed.
 
737
        """
 
738
        self.disconnecting = 1
 
739
        self.stopReading()
 
740
        if self.connected:
 
741
            self.deferred = defer.Deferred()
 
742
            self.reactor.callLater(0, self.connectionLost, connDone)
 
743
            return self.deferred
 
744
 
 
745
    stopListening = loseConnection
 
746
 
 
747
    def connectionLost(self, reason):
 
748
        """Cleans up my socket.
 
749
        """
 
750
        log.msg('(Port %r Closed)' % self.port)
 
751
        base.BasePort.connectionLost(self, reason)
 
752
        self.connected = 0
 
753
        self.socket.close()
 
754
        del self.socket
 
755
        del self.fileno
 
756
        self.factory.doStop()
 
757
        if hasattr(self, "deferred"):
 
758
            self.deferred.callback(None)
 
759
            del self.deferred
 
760
 
 
761
    def logPrefix(self):
 
762
        """Returns the name of my class, to prefix log entries with.
 
763
        """
 
764
        return reflect.qual(self.factory.__class__)
 
765
 
 
766
    def getHost(self):
 
767
        """Returns a tuple of ('INET', hostname, port).
 
768
 
 
769
        This indicates the server's address.
 
770
        """
 
771
        return ('INET',)+self.socket.getsockname()
 
772
 
 
773
 
 
774
class Connector(base.BaseConnector):
 
775
    def __init__(self, host, port, factory, timeout, bindAddress, reactor=None):
 
776
        self.host = host
 
777
        if isinstance(port, types.StringTypes):
 
778
            try:
 
779
                port = socket.getservbyname(port, 'tcp')
 
780
            except socket.error, e:
 
781
                raise error.ServiceNameUnknownError(string=str(e))
 
782
        self.port = port
 
783
        self.bindAddress = bindAddress
 
784
        base.BaseConnector.__init__(self, factory, timeout, reactor)
 
785
 
 
786
    def _makeTransport(self):
 
787
        return Client(self.host, self.port, self.bindAddress, self, self.reactor)
 
788
 
 
789
    def getDestination(self):
 
790
        return ('INET', self.host, self.port)