~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/internet/iocpreactor/tcp.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2008-2009 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
"""
 
5
TCP support for IOCP reactor
 
6
"""
 
7
 
 
8
import socket, operator, errno, struct
 
9
 
 
10
from zope.interface import implements, directlyProvides
 
11
 
 
12
from twisted.internet import interfaces, error, address, main, defer
 
13
from twisted.internet.abstract import isIPAddress
 
14
from twisted.internet.tcp import _SocketCloser, Connector as TCPConnector
 
15
from twisted.persisted import styles
 
16
from twisted.python import log, failure, reflect, util
 
17
 
 
18
from twisted.internet.iocpreactor import iocpsupport as _iocp, abstract
 
19
from twisted.internet.iocpreactor.interfaces import IReadWriteHandle
 
20
from twisted.internet.iocpreactor.const import ERROR_IO_PENDING
 
21
from twisted.internet.iocpreactor.const import SO_UPDATE_CONNECT_CONTEXT
 
22
from twisted.internet.iocpreactor.const import SO_UPDATE_ACCEPT_CONTEXT
 
23
from twisted.internet.iocpreactor.const import ERROR_CONNECTION_REFUSED
 
24
from twisted.internet.iocpreactor.const import ERROR_NETWORK_UNREACHABLE
 
25
 
 
26
try:
 
27
    from twisted.protocols.tls import TLSMemoryBIOFactory, TLSMemoryBIOProtocol
 
28
except ImportError:
 
29
    TLSMemoryBIOProtocol = TLSMemoryBIOFactory = None
 
30
    _extraInterfaces = ()
 
31
else:
 
32
    _extraInterfaces = (interfaces.ITLSTransport,)
 
33
 
 
34
# ConnectEx returns these. XXX: find out what it does for timeout
 
35
connectExErrors = {
 
36
        ERROR_CONNECTION_REFUSED: errno.WSAECONNREFUSED,
 
37
        ERROR_NETWORK_UNREACHABLE: errno.WSAENETUNREACH,
 
38
        }
 
39
 
 
40
 
 
41
class _BypassTLS(object):
 
42
    """
 
43
    L{_BypassTLS} is used as the transport object for the TLS protocol object
 
44
    used to implement C{startTLS}.  Its methods skip any TLS logic which
 
45
    C{startTLS} enables.
 
46
 
 
47
    @ivar _connection: A L{Connection} which TLS has been started on which will
 
48
        be proxied to by this object.  Any method which has its behavior
 
49
        altered after C{startTLS} will be skipped in favor of the base class's
 
50
        implementation.  This allows the TLS protocol object to have direct
 
51
        access to the transport, necessary to actually implement TLS.
 
52
    """
 
53
    def __init__(self, connection):
 
54
        self._connection = connection
 
55
 
 
56
 
 
57
    def __getattr__(self, name):
 
58
        return getattr(self._connection, name)
 
59
 
 
60
 
 
61
    def write(self, data):
 
62
        return abstract.FileHandle.write(self._connection, data)
 
63
 
 
64
 
 
65
    def writeSequence(self, iovec):
 
66
        return abstract.FileHandle.writeSequence(self._connection, iovec)
 
67
 
 
68
 
 
69
    def loseConnection(self, reason=None):
 
70
        return abstract.FileHandle.loseConnection(self._connection, reason)
 
71
 
 
72
 
 
73
 
 
74
class Connection(abstract.FileHandle, _SocketCloser):
 
75
    """
 
76
    @ivar _tls: C{False} to indicate the connection is in normal TCP mode,
 
77
        C{True} to indicate that TLS has been started and that operations must
 
78
        be routed through the L{TLSMemoryBIOProtocol} instance.
 
79
 
 
80
    @ivar _tlsClientDefault: A flag which must be set by a subclass.  If set to
 
81
        C{True}, L{startTLS} will default to initiating SSL as a client.  If
 
82
        set to C{False}, L{startTLS} will default to initiating SSL as a
 
83
        server.
 
84
    """
 
85
    implements(IReadWriteHandle, interfaces.ITCPTransport,
 
86
               interfaces.ISystemHandle, *_extraInterfaces)
 
87
 
 
88
    _tls = False
 
89
 
 
90
    def __init__(self, sock, proto, reactor=None):
 
91
        abstract.FileHandle.__init__(self, reactor)
 
92
        self.socket = sock
 
93
        self.getFileHandle = sock.fileno
 
94
        self.protocol = proto
 
95
 
 
96
 
 
97
    def getHandle(self):
 
98
        return self.socket
 
99
 
 
100
 
 
101
    def dataReceived(self, rbuffer):
 
102
        # XXX: some day, we'll have protocols that can handle raw buffers
 
103
        self.protocol.dataReceived(str(rbuffer))
 
104
 
 
105
 
 
106
    def readFromHandle(self, bufflist, evt):
 
107
        return _iocp.recv(self.getFileHandle(), bufflist, evt)
 
108
 
 
109
 
 
110
    def writeToHandle(self, buff, evt):
 
111
        return _iocp.send(self.getFileHandle(), buff, evt)
 
112
 
 
113
 
 
114
    def _closeWriteConnection(self):
 
115
        try:
 
116
            getattr(self.socket, self._socketShutdownMethod)(1)
 
117
        except socket.error:
 
118
            pass
 
119
        p = interfaces.IHalfCloseableProtocol(self.protocol, None)
 
120
        if p:
 
121
            try:
 
122
                p.writeConnectionLost()
 
123
            except:
 
124
                f = failure.Failure()
 
125
                log.err()
 
126
                self.connectionLost(f)
 
127
 
 
128
 
 
129
    def readConnectionLost(self, reason):
 
130
        p = interfaces.IHalfCloseableProtocol(self.protocol, None)
 
131
        if p:
 
132
            try:
 
133
                p.readConnectionLost()
 
134
            except:
 
135
                log.err()
 
136
                self.connectionLost(failure.Failure())
 
137
        else:
 
138
            self.connectionLost(reason)
 
139
 
 
140
 
 
141
    def connectionLost(self, reason):
 
142
        abstract.FileHandle.connectionLost(self, reason)
 
143
        self._closeSocket()
 
144
        protocol = self.protocol
 
145
        del self.protocol
 
146
        del self.socket
 
147
        del self.getFileHandle
 
148
        protocol.connectionLost(reason)
 
149
 
 
150
 
 
151
    def logPrefix(self):
 
152
        """
 
153
        Return the prefix to log with when I own the logging thread.
 
154
        """
 
155
        return self.logstr
 
156
 
 
157
 
 
158
    def getTcpNoDelay(self):
 
159
        return operator.truth(self.socket.getsockopt(socket.IPPROTO_TCP,
 
160
                                                     socket.TCP_NODELAY))
 
161
 
 
162
 
 
163
    def setTcpNoDelay(self, enabled):
 
164
        self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, enabled)
 
165
 
 
166
 
 
167
    def getTcpKeepAlive(self):
 
168
        return operator.truth(self.socket.getsockopt(socket.SOL_SOCKET,
 
169
                                                     socket.SO_KEEPALIVE))
 
170
 
 
171
 
 
172
    def setTcpKeepAlive(self, enabled):
 
173
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, enabled)
 
174
 
 
175
 
 
176
    if TLSMemoryBIOFactory is not None:
 
177
        def startTLS(self, contextFactory, normal=True):
 
178
            """
 
179
            @see: L{ITLSTransport.startTLS}
 
180
            """
 
181
            # Figure out which direction the SSL goes in.  If normal is True,
 
182
            # we'll go in the direction indicated by the subclass.  Otherwise,
 
183
            # we'll go the other way (client = not normal ^ _tlsClientDefault,
 
184
            # in other words).
 
185
            if normal:
 
186
                client = self._tlsClientDefault
 
187
            else:
 
188
                client = not self._tlsClientDefault
 
189
 
 
190
            tlsFactory = TLSMemoryBIOFactory(contextFactory, client, None)
 
191
            tlsProtocol = TLSMemoryBIOProtocol(tlsFactory, self.protocol, False)
 
192
            self.protocol = tlsProtocol
 
193
 
 
194
            self.getHandle = tlsProtocol.getHandle
 
195
            self.getPeerCertificate = tlsProtocol.getPeerCertificate
 
196
 
 
197
            # Mark the transport as secure.
 
198
            directlyProvides(self, interfaces.ISSLTransport)
 
199
 
 
200
            # Remember we did this so that write and writeSequence can send the
 
201
            # data to the right place.
 
202
            self._tls = True
 
203
 
 
204
            # Hook it up
 
205
            self.protocol.makeConnection(_BypassTLS(self))
 
206
 
 
207
 
 
208
    def write(self, data):
 
209
        """
 
210
        Write some data, either directly to the underlying handle or, if TLS
 
211
        has been started, to the L{TLSMemoryBIOProtocol} for it to encrypt and
 
212
        send.
 
213
 
 
214
        @see: L{ITCPTransport.write}
 
215
        """
 
216
        if self._tls:
 
217
            self.protocol.write(data)
 
218
        else:
 
219
            abstract.FileHandle.write(self, data)
 
220
 
 
221
 
 
222
    def writeSequence(self, iovec):
 
223
        """
 
224
        Write some data, either directly to the underlying handle or, if TLS
 
225
        has been started, to the L{TLSMemoryBIOProtocol} for it to encrypt and
 
226
        send.
 
227
 
 
228
        @see: L{ITCPTransport.writeSequence}
 
229
        """
 
230
        if self._tls:
 
231
            self.protocol.writeSequence(iovec)
 
232
        else:
 
233
            abstract.FileHandle.writeSequence(self, iovec)
 
234
 
 
235
 
 
236
    def loseConnection(self, reason=None):
 
237
        """
 
238
        Close the underlying handle or, if TLS has been started, first shut it
 
239
        down.
 
240
 
 
241
        @see: L{ITCPTransport.loseConnection}
 
242
        """
 
243
        if self._tls:
 
244
            if self.connected and not self.disconnecting:
 
245
                self.protocol.loseConnection()
 
246
        else:
 
247
            abstract.FileHandle.loseConnection(self, reason)
 
248
 
 
249
 
 
250
 
 
251
class Client(Connection):
 
252
    addressFamily = socket.AF_INET
 
253
    socketType = socket.SOCK_STREAM
 
254
 
 
255
    _tlsClientDefault = True
 
256
 
 
257
    def __init__(self, host, port, bindAddress, connector, reactor):
 
258
        self.connector = connector
 
259
        self.addr = (host, port)
 
260
        self.reactor = reactor
 
261
        # ConnectEx documentation says socket _has_ to be bound
 
262
        if bindAddress is None:
 
263
            bindAddress = ('', 0)
 
264
 
 
265
        try:
 
266
            try:
 
267
                skt = reactor.createSocket(self.addressFamily, self.socketType)
 
268
            except socket.error, se:
 
269
                raise error.ConnectBindError(se[0], se[1])
 
270
            else:
 
271
                try:
 
272
                    skt.bind(bindAddress)
 
273
                except socket.error, se:
 
274
                    raise error.ConnectBindError(se[0], se[1])
 
275
                self.socket = skt
 
276
                Connection.__init__(self, skt, None, reactor)
 
277
                reactor.callLater(0, self.resolveAddress)
 
278
        except error.ConnectBindError, err:
 
279
            reactor.callLater(0, self.failIfNotConnected, err)
 
280
 
 
281
 
 
282
    def resolveAddress(self):
 
283
        if isIPAddress(self.addr[0]):
 
284
            self._setRealAddress(self.addr[0])
 
285
        else:
 
286
            d = self.reactor.resolve(self.addr[0])
 
287
            d.addCallbacks(self._setRealAddress, self.failIfNotConnected)
 
288
 
 
289
 
 
290
    def _setRealAddress(self, address):
 
291
        self.realAddress = (address, self.addr[1])
 
292
        self.doConnect()
 
293
 
 
294
 
 
295
    def failIfNotConnected(self, err):
 
296
        if (self.connected or self.disconnected or
 
297
            not hasattr(self, "connector")):
 
298
            return
 
299
 
 
300
        try:
 
301
            self._closeSocket()
 
302
        except AttributeError:
 
303
            pass
 
304
        else:
 
305
            del self.socket, self.getFileHandle
 
306
        self.reactor.removeActiveHandle(self)
 
307
 
 
308
        self.connector.connectionFailed(failure.Failure(err))
 
309
        del self.connector
 
310
 
 
311
 
 
312
    def stopConnecting(self):
 
313
        """
 
314
        Stop attempt to connect.
 
315
        """
 
316
        self.failIfNotConnected(error.UserError())
 
317
 
 
318
 
 
319
    def cbConnect(self, rc, bytes, evt):
 
320
        if rc:
 
321
            rc = connectExErrors.get(rc, rc)
 
322
            self.failIfNotConnected(error.getConnectError((rc,
 
323
                                    errno.errorcode.get(rc, 'Unknown error'))))
 
324
        else:
 
325
            self.socket.setsockopt(socket.SOL_SOCKET,
 
326
                                   SO_UPDATE_CONNECT_CONTEXT,
 
327
                                   struct.pack('I', self.socket.fileno()))
 
328
            self.protocol = self.connector.buildProtocol(self.getPeer())
 
329
            self.connected = True
 
330
            self.logstr = self.protocol.__class__.__name__+",client"
 
331
            self.protocol.makeConnection(self)
 
332
            self.startReading()
 
333
 
 
334
 
 
335
    def doConnect(self):
 
336
        if not hasattr(self, "connector"):
 
337
            # this happens if we connector.stopConnecting in
 
338
            # factory.startedConnecting
 
339
            return
 
340
        assert _iocp.have_connectex
 
341
        self.reactor.addActiveHandle(self)
 
342
        evt = _iocp.Event(self.cbConnect, self)
 
343
 
 
344
        rc = _iocp.connect(self.socket.fileno(), self.realAddress, evt)
 
345
        if rc == ERROR_IO_PENDING:
 
346
            return
 
347
        else:
 
348
            evt.ignore = True
 
349
            self.cbConnect(rc, 0, 0, evt)
 
350
 
 
351
 
 
352
    def getHost(self):
 
353
        """
 
354
        Returns an IPv4Address.
 
355
 
 
356
        This indicates the address from which I am connecting.
 
357
        """
 
358
        return address.IPv4Address('TCP', *(self.socket.getsockname() +
 
359
                                            ('INET',)))
 
360
 
 
361
 
 
362
    def getPeer(self):
 
363
        """
 
364
        Returns an IPv4Address.
 
365
 
 
366
        This indicates the address that I am connected to.
 
367
        """
 
368
        return address.IPv4Address('TCP', *(self.realAddress + ('INET',)))
 
369
 
 
370
 
 
371
    def __repr__(self):
 
372
        s = ('<%s to %s at %x>' %
 
373
                (self.__class__, self.addr, util.unsignedID(self)))
 
374
        return s
 
375
 
 
376
 
 
377
    def connectionLost(self, reason):
 
378
        if not self.connected:
 
379
            self.failIfNotConnected(error.ConnectError(string=reason))
 
380
        else:
 
381
            Connection.connectionLost(self, reason)
 
382
            self.connector.connectionLost(reason)
 
383
 
 
384
 
 
385
 
 
386
class Server(Connection):
 
387
    """
 
388
    Serverside socket-stream connection class.
 
389
 
 
390
    I am a serverside network connection transport; a socket which came from an
 
391
    accept() on a server.
 
392
    """
 
393
 
 
394
    _tlsClientDefault = False
 
395
 
 
396
 
 
397
    def __init__(self, sock, protocol, clientAddr, serverAddr, sessionno, reactor):
 
398
        """
 
399
        Server(sock, protocol, client, server, sessionno)
 
400
 
 
401
        Initialize me with a socket, a protocol, a descriptor for my peer (a
 
402
        tuple of host, port describing the other end of the connection), an
 
403
        instance of Port, and a session number.
 
404
        """
 
405
        Connection.__init__(self, sock, protocol, reactor)
 
406
        self.serverAddr = serverAddr
 
407
        self.clientAddr = clientAddr
 
408
        self.sessionno = sessionno
 
409
        self.logstr = "%s,%s,%s" % (self.protocol.__class__.__name__,
 
410
                                    sessionno, self.clientAddr.host)
 
411
        self.repstr = "<%s #%s on %s>" % (self.protocol.__class__.__name__,
 
412
                                          self.sessionno, self.serverAddr.port)
 
413
        self.connected = True
 
414
        self.startReading()
 
415
 
 
416
 
 
417
    def __repr__(self):
 
418
        """
 
419
        A string representation of this connection.
 
420
        """
 
421
        return self.repstr
 
422
 
 
423
 
 
424
    def getHost(self):
 
425
        """
 
426
        Returns an IPv4Address.
 
427
 
 
428
        This indicates the server's address.
 
429
        """
 
430
        return self.serverAddr
 
431
 
 
432
 
 
433
    def getPeer(self):
 
434
        """
 
435
        Returns an IPv4Address.
 
436
 
 
437
        This indicates the client's address.
 
438
        """
 
439
        return self.clientAddr
 
440
 
 
441
 
 
442
 
 
443
class Connector(TCPConnector):
 
444
    def _makeTransport(self):
 
445
        return Client(self.host, self.port, self.bindAddress, self,
 
446
                      self.reactor)
 
447
 
 
448
 
 
449
 
 
450
class Port(styles.Ephemeral, _SocketCloser):
 
451
    implements(interfaces.IListeningPort)
 
452
 
 
453
    connected = False
 
454
    disconnected = False
 
455
    disconnecting = False
 
456
    addressFamily = socket.AF_INET
 
457
    socketType = socket.SOCK_STREAM
 
458
 
 
459
    sessionno = 0
 
460
 
 
461
    maxAccepts = 100
 
462
 
 
463
    # Actual port number being listened on, only set to a non-None
 
464
    # value when we are actually listening.
 
465
    _realPortNumber = None
 
466
 
 
467
 
 
468
    def __init__(self, port, factory, backlog=50, interface='', reactor=None):
 
469
        self.port = port
 
470
        self.factory = factory
 
471
        self.backlog = backlog
 
472
        self.interface = interface
 
473
        self.reactor = reactor
 
474
 
 
475
 
 
476
    def __repr__(self):
 
477
        if self._realPortNumber is not None:
 
478
            return "<%s of %s on %s>" % (self.__class__,
 
479
                                         self.factory.__class__,
 
480
                                         self._realPortNumber)
 
481
        else:
 
482
            return "<%s of %s (not listening)>" % (self.__class__,
 
483
                                                   self.factory.__class__)
 
484
 
 
485
 
 
486
    def startListening(self):
 
487
        try:
 
488
            skt = self.reactor.createSocket(self.addressFamily,
 
489
                                            self.socketType)
 
490
            # TODO: resolve self.interface if necessary
 
491
            skt.bind((self.interface, self.port))
 
492
        except socket.error, le:
 
493
            raise error.CannotListenError, (self.interface, self.port, le)
 
494
 
 
495
        self.addrLen = _iocp.maxAddrLen(skt.fileno())
 
496
 
 
497
        # Make sure that if we listened on port 0, we update that to
 
498
        # reflect what the OS actually assigned us.
 
499
        self._realPortNumber = skt.getsockname()[1]
 
500
 
 
501
        log.msg("%s starting on %s" % (self.factory.__class__,
 
502
                                       self._realPortNumber))
 
503
 
 
504
        self.factory.doStart()
 
505
        skt.listen(self.backlog)
 
506
        self.connected = True
 
507
        self.disconnected = False
 
508
        self.reactor.addActiveHandle(self)
 
509
        self.socket = skt
 
510
        self.getFileHandle = self.socket.fileno
 
511
        self.doAccept()
 
512
 
 
513
 
 
514
    def loseConnection(self, connDone=failure.Failure(main.CONNECTION_DONE)):
 
515
        """
 
516
        Stop accepting connections on this port.
 
517
 
 
518
        This will shut down my socket and call self.connectionLost().
 
519
        It returns a deferred which will fire successfully when the
 
520
        port is actually closed.
 
521
        """
 
522
        self.disconnecting = True
 
523
        if self.connected:
 
524
            self.deferred = defer.Deferred()
 
525
            self.reactor.callLater(0, self.connectionLost, connDone)
 
526
            return self.deferred
 
527
 
 
528
    stopListening = loseConnection
 
529
 
 
530
 
 
531
    def connectionLost(self, reason):
 
532
        """
 
533
        Cleans up the socket.
 
534
        """
 
535
        log.msg('(Port %s Closed)' % self._realPortNumber)
 
536
        self._realPortNumber = None
 
537
        d = None
 
538
        if hasattr(self, "deferred"):
 
539
            d = self.deferred
 
540
            del self.deferred
 
541
 
 
542
        self.disconnected = True
 
543
        self.reactor.removeActiveHandle(self)
 
544
        self.connected = False
 
545
        self._closeSocket()
 
546
        del self.socket
 
547
        del self.getFileHandle
 
548
 
 
549
        try:
 
550
            self.factory.doStop()
 
551
        except:
 
552
            self.disconnecting = False
 
553
            if d is not None:
 
554
                d.errback(failure.Failure())
 
555
            else:
 
556
                raise
 
557
        else:
 
558
            self.disconnecting = False
 
559
            if d is not None:
 
560
                d.callback(None)
 
561
 
 
562
 
 
563
    def logPrefix(self):
 
564
        """
 
565
        Returns the name of my class, to prefix log entries with.
 
566
        """
 
567
        return reflect.qual(self.factory.__class__)
 
568
 
 
569
 
 
570
    def getHost(self):
 
571
        """
 
572
        Returns an IPv4Address.
 
573
 
 
574
        This indicates the server's address.
 
575
        """
 
576
        return address.IPv4Address('TCP', *(self.socket.getsockname() +
 
577
                                            ('INET',)))
 
578
 
 
579
 
 
580
    def cbAccept(self, rc, bytes, evt):
 
581
        self.handleAccept(rc, evt)
 
582
        if not (self.disconnecting or self.disconnected):
 
583
            self.doAccept()
 
584
 
 
585
 
 
586
    def handleAccept(self, rc, evt):
 
587
        if self.disconnecting or self.disconnected:
 
588
            return False
 
589
 
 
590
        # possible errors:
 
591
        # (WSAEMFILE, WSAENOBUFS, WSAENFILE, WSAENOMEM, WSAECONNABORTED)
 
592
        if rc:
 
593
            log.msg("Could not accept new connection -- %s (%s)" %
 
594
                    (errno.errorcode.get(rc, 'unknown error'), rc))
 
595
            return False
 
596
        else:
 
597
            evt.newskt.setsockopt(socket.SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
 
598
                                  struct.pack('I', self.socket.fileno()))
 
599
            family, lAddr, rAddr = _iocp.get_accept_addrs(evt.newskt.fileno(),
 
600
                                                          evt.buff)
 
601
            assert family == self.addressFamily
 
602
 
 
603
            protocol = self.factory.buildProtocol(
 
604
                address._ServerFactoryIPv4Address('TCP', rAddr[0], rAddr[1]))
 
605
            if protocol is None:
 
606
                evt.newskt.close()
 
607
            else:
 
608
                s = self.sessionno
 
609
                self.sessionno = s+1
 
610
                transport = Server(evt.newskt, protocol,
 
611
                        address.IPv4Address('TCP', rAddr[0], rAddr[1], 'INET'),
 
612
                        address.IPv4Address('TCP', lAddr[0], lAddr[1], 'INET'),
 
613
                        s, self.reactor)
 
614
                protocol.makeConnection(transport)
 
615
            return True
 
616
 
 
617
 
 
618
    def doAccept(self):
 
619
        numAccepts = 0
 
620
        while 1:
 
621
            evt = _iocp.Event(self.cbAccept, self)
 
622
 
 
623
            # see AcceptEx documentation
 
624
            evt.buff = buff = _iocp.AllocateReadBuffer(2 * (self.addrLen + 16))
 
625
 
 
626
            evt.newskt = newskt = self.reactor.createSocket(self.addressFamily,
 
627
                                                            self.socketType)
 
628
            rc = _iocp.accept(self.socket.fileno(), newskt.fileno(), buff, evt)
 
629
 
 
630
            if (rc == ERROR_IO_PENDING
 
631
                or (not rc and numAccepts >= self.maxAccepts)):
 
632
                break
 
633
            else:
 
634
                evt.ignore = True
 
635
                if not self.handleAccept(rc, evt):
 
636
                    break
 
637
            numAccepts += 1
 
638
 
 
639