def _hostpeertest(self, get, testServer): """ Test one of the permutations of client/server host/peer. """ class TestProtocol(Protocol): def makeConnection(self, transport): Protocol.makeConnection(self, transport) self.onConnection.callback(transport) if testServer: server = TestProtocol() d = server.onConnection = Deferred() client = Protocol() else: server = Protocol() client = TestProtocol() d = client.onConnection = Deferred() loopback.loopbackAsync(server, client) def connected(transport): host = getattr(transport, get)() self.assertTrue(IAddress.providedBy(host)) return d.addCallback(connected)
def test_pumpPolicy(self): """ The callable passed as the value for the C{pumpPolicy} parameter to L{loopbackAsync} is called with a L{_LoopbackQueue} of pending bytes and a protocol to which they should be delivered. """ pumpCalls = [] def dummyPolicy(queue, target): bytes = [] while queue: bytes.append(queue.get()) pumpCalls.append((target, bytes)) client = Protocol() server = Protocol() finished = loopback.loopbackAsync(server, client, dummyPolicy) self.assertEquals(pumpCalls, []) client.transport.write("foo") client.transport.write("bar") server.transport.write("baz") server.transport.write("quux") server.transport.loseConnection() def cbComplete(ignored): self.assertEquals( pumpCalls, # The order here is somewhat arbitrary. The implementation # happens to always deliver data to the client first. [(client, ["baz", "quux", None]), (server, ["foo", "bar"])]) finished.addCallback(cbComplete) return finished
def _testStreamingProducer(self, mode): """ Connect a couple protocol/transport pairs to an L{IOPump} and then pump it. Verify that a streaming producer registered with one of the transports does not receive invalid L{IPushProducer} method calls and ends in the right state. @param mode: C{u"server"} to test a producer registered with the server transport. C{u"client"} to test a producer registered with the client transport. """ serverProto = Protocol() serverTransport = FakeTransport(serverProto, isServer=True) clientProto = Protocol() clientTransport = FakeTransport(clientProto, isServer=False) pump = connect( serverProto, serverTransport, clientProto, clientTransport, greet=False, ) producer = StrictPushProducer() victim = { u"server": serverTransport, u"client": clientTransport, }[mode] victim.registerProducer(producer, streaming=True) pump.pump() self.assertEqual(u"running", producer._state)
def test_removeAll(self): """ Calling C{removeAll} on a reactor includes descriptors that are filesystem files. """ reactor = self.buildReactor() self.addCleanup(self.unbuildReactor, reactor) path = self.mktemp() open(path, "wb").close() # Cleanup might fail if file is GCed too soon: self.f = f = open(path, "rb") # Have the reader added: stdio = StandardIO( Protocol(), stdin=f.fileno(), stdout=self.extraFile.fileno(), reactor=reactor, ) # And then removed: removed = reactor.removeAll() self.assertIn(stdio._reader, removed) self.assertNotIn(stdio._reader, reactor.getReaders())
def find_thermostats(self): """ Scan the subnet to find all the thermostats or anything serving on the port 8888 """ ip, port = self.address.split(':') subnet = ip.split(".")[0:2] ds = [] for i in range(255): point = TCP4ClientEndpoint(reactor, ".".join(subnet+[i]), port) d = connectProtocol(point, Protocol()) d.addTimeout(1, reactor) ds.append(d) def on_results(results): addrs = [] for i, result in enumerate(results): success, p = result if success: addrs.append("{}:{}".format( ".".join(subnet+[i]), port)) p.transport.loseConnection() self.addresses = addrs DeferredList(ds, consumeErrors=True).addCallback(on_results)
def scan_subnet(self): """ Scan to find any ws clients on the port""" subnet = self.address.split('.')[0:3] addrs = [] def on_connect(p, addr): log.debug("scan | {}:{} is up!".format(addr, self.port)) addrs.append(addr) p.transport.loseConnection() #: Scan for everything in the subnet #: timeout after 1 second ds = [] for i in range(256): addr = ".".join(subnet + [str(i)]) point = TCP4ClientEndpoint(reactor, addr, self.port) d = connectProtocol(point, Protocol()) d.addCallback(lambda p, addr=addr: on_connect(p, addr)) d.addErrback(lambda e, addr=addr: log.debug("scan | {} is down". format(addr))) reactor.callLater(1, d.cancel) ds.append(d) #: When they all finish update the addresses property done = DeferredList(ds) done.addCallback(lambda r: setattr(self, 'addresses', addrs))
def test_renderSecureRequest(self): """ When the rendered request is over HTTPS, L{WebSocketsResource} wraps the protocol of the C{TLSMemoryBIOProtocol} instance. """ request = DummyRequest("/") request.requestHeaders = Headers() transport = StringTransportWithDisconnection() secureProtocol = TLSMemoryBIOProtocol(Factory(), Protocol()) transport.protocol = secureProtocol request.transport = transport request.headers.update({ "upgrade": "Websocket", "connection": "Upgrade", "sec-websocket-key": "secure", "sec-websocket-version": "13" }) result = self.resource.render(request) self.assertEqual(NOT_DONE_YET, result) self.assertEqual( { "connection": "Upgrade", "upgrade": "WebSocket", "sec-websocket-accept": "oYBv54i42V5dw6KnZqOFroecUTc=" }, request.outgoingHeaders) self.assertEqual([""], request.written) self.assertEqual(101, request.responseCode) self.assertIdentical(None, request.transport) self.assertIsInstance(transport.protocol.wrappedProtocol, WebSocketsProtocol) self.assertIsInstance(transport.protocol.wrappedProtocol._receiver, SavingEchoReceiver)
def test_disorderlyShutdown(self): """ If a L{TLSMemoryBIOProtocol} loses its connection unexpectedly, this is reported to the application. """ clientConnectionLost = Deferred() clientFactory = ClientFactory() clientFactory.protocol = ( lambda: ConnectionLostNotifyingProtocol(clientConnectionLost)) clientContextFactory = HandshakeCallbackContextFactory() wrapperFactory = TLSMemoryBIOFactory(clientContextFactory, True, clientFactory) sslClientProtocol = wrapperFactory.buildProtocol(None) # Client speaks first, so the server can be dumb. serverProtocol = Protocol() connectionDeferred = loopbackAsync(serverProtocol, sslClientProtocol) # Now destroy the connection. serverProtocol.transport.loseConnection() # And when the connection completely dies, check the reason. def cbDisconnected(clientProtocol): clientProtocol.lostConnectionReason.trap(Error) clientConnectionLost.addCallback(cbDisconnected) return clientConnectionLost
def request_shell(self, data): protocol = Protocol() transport = SSHSessionProcessProtocol(self) protocol.makeConnection(transport) transport.makeConnection(wrapProtocol(protocol)) self.client = transport return True
def cbConnect(self, rc, data, evt): if rc: rc = connectExErrors.get(rc, rc) self.failIfNotConnected( error.getConnectError( (rc, errno.errorcode.get(rc, "Unknown error")))) else: self.socket.setsockopt( socket.SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, struct.pack("P", self.socket.fileno()), ) self.protocol = self.connector.buildProtocol(self.getPeer()) self.connected = True logPrefix = self._getLogPrefix(self.protocol) self.logstr = logPrefix + ",client" if self.protocol is None: # Factory.buildProtocol is allowed to return None. In that # case, make up a protocol to satisfy the rest of the # implementation; connectionLost is going to be called on # something, for example. This is easier than adding special # case support for a None protocol throughout the rest of the # transport implementation. self.protocol = Protocol() # But dispose of the connection quickly. self.loseConnection() else: self.protocol.makeConnection(self) self.startReading()
def test_interfaces(self): """ L{Protocol} instances provide L{IProtocol} and L{ILoggingContext}. """ proto = Protocol() self.assertTrue(verifyObject(IProtocol, proto)) self.assertTrue(verifyObject(ILoggingContext, proto))
def test_renderIProtocol(self): """ If the protocol returned by C{lookupProtocol} isn't a C{WebSocketsProtocol}, L{WebSocketsResource} wraps it automatically with L{WebSocketsProtocolWrapper}. """ def lookupProtocol(names, otherRequest): return AccumulatingProtocol(), None self.resource = WebSocketsResource(lookupProtocol) request = DummyRequest(b"/") request.requestHeaders = Headers({ b"user-agent": [b"user-agent"], b"host": [b"host"], }) transport = StringTransportWithDisconnection() transport.protocol = Protocol() request.transport = transport self.update_headers(request, headers={ b"upgrade": b"Websocket", b"connection": b"Upgrade", b"sec-websocket-key": b"secure", b"sec-websocket-version": b"13" }) result = self.resource.render(request) self.assertEqual(NOT_DONE_YET, result) self.assertIsInstance(transport.protocol, WebSocketsProtocolWrapper) self.assertIsInstance(transport.protocol.wrappedProtocol, AccumulatingProtocol)
def build_protocol(): """ :return: ``Protocol`` hooked up to transport. """ p = Protocol() p.makeConnection(StringTransport()) return p
def test_render(self): """ When rendering a request, L{WebSocketsResource} uses the C{Sec-WebSocket-Key} header to generate a C{Sec-WebSocket-Accept} value. It creates a L{WebSocketsProtocol} instance connected to the protocol provided by the user factory. """ request = DummyRequest("/") request.requestHeaders = Headers() transport = StringTransportWithDisconnection() transport.protocol = Protocol() request.transport = transport request.headers.update({ "upgrade": "Websocket", "connection": "Upgrade", "sec-websocket-key": "secure", "sec-websocket-version": "13" }) result = self.resource.render(request) self.assertEqual(NOT_DONE_YET, result) self.assertEqual( { "connection": "Upgrade", "upgrade": "WebSocket", "sec-websocket-accept": "oYBv54i42V5dw6KnZqOFroecUTc=" }, request.outgoingHeaders) self.assertEqual([""], request.written) self.assertEqual(101, request.responseCode) self.assertIdentical(None, request.transport) self.assertIsInstance(transport.protocol._receiver, SavingEchoReceiver)
def _connectDone(self): """ This is a hook for when a connection attempt has succeeded. Here, we build the protocol from the L{twisted.internet.protocol.ClientFactory} that was passed in, compute a log string, begin reading so as to send traffic to the newly built protocol, and finally hook up the protocol itself. This hook is overridden by L{ssl.Client} to initiate the TLS protocol. """ self.protocol = self.connector.buildProtocol(self.getPeer()) self.connected = 1 logPrefix = self._getLogPrefix(self.protocol) self.logstr = "%s,client" % logPrefix if self.protocol is None: # Factory.buildProtocol is allowed to return None. In that case, # make up a protocol to satisfy the rest of the implementation; # connectionLost is going to be called on something, for example. # This is easier than adding special case support for a None # protocol throughout the rest of the transport implementation. self.protocol = Protocol() # But dispose of the connection quickly. self.loseConnection() else: self.startReading() self.protocol.makeConnection(self)
def setUp(self): """ Create a L{conncache.ConnectionCache}, endpoint and protocol to test against. """ self.cache = conncache.ConnectionCache() self.endpoint = FakeEndpoint() self.protocol = Protocol()
def test_unregisterProtocol_before_stopFactory(self): """Connections can go away before stopFactory occurs without causing errors. """ ctf = gracefulshutdown.ConnTrackingFactoryWrapper(Factory()) p = Protocol() ctf.registerProtocol(p) ctf.unregisterProtocol(p) # No error raised.
def test_loseConnectionCodeAndReason(self): """ L{WebSocketsTransport.loseConnection} accepts a code and a reason which are used to build the closing frame. """ transport = StringTransportWithDisconnection() transport.protocol = Protocol() webSocketsTranport = WebSocketsTransport(transport) webSocketsTranport.loseConnection(STATUSES.GOING_AWAY, b"Going away") self.assertEqual(b"\x88\x0c\x03\xe9Going away", transport.value())
def setUp(self): # Re-usable protocol and reactor self.protocol = Protocol() self.reactor = DoNothing() self.directory = tempfile.mkdtemp() self.path = os.path.join(self.directory, 'fake_serial') data = b'1234' with open(self.path, 'wb') as f: f.write(data)
def __init__(self, message_object, factory): """ :param factory: instance of factory creating this protocol class :param message_object: an instance of the SpokenMessages class (must have speak(), listen(), follow_up()) implemented """ Protocol().__init__() self.factory = factory self.message_object = message_object assert (isinstance(self.message_object, SpokenMessages))
def test_protocolToConsumer(self): """ L{IProtocol} providers can be adapted to L{IConsumer} providers using L{ProtocolToConsumerAdapter}. """ result = [] p = Protocol() p.dataReceived = result.append consumer = IConsumer(p) consumer.write(b"hello") self.assertEqual(result, [b"hello"]) self.assertIsInstance(consumer, ProtocolToConsumerAdapter)
def test_startService(self): """ L{StandardIOService.startService} connects a protocol to a standard io transport. """ # This sucks. StandardIO sucks. APIs should be testable. L = [] self.patch(process, 'StandardIO', L.append) proto = Protocol() service = process.StandardIOService(proto) service.startService() self.assertEqual(L, [proto])
def test_loseConnectionAfterHandshake(self): """ L{TLSMemoryBIOProtocol.loseConnection} sends a TLS close alert and shuts down the underlying connection. """ clientConnectionLost = Deferred() clientFactory = ClientFactory() clientFactory.protocol = ( lambda: ConnectionLostNotifyingProtocol(clientConnectionLost)) clientContextFactory, handshakeDeferred = ( HandshakeCallbackContextFactory.factoryAndDeferred()) wrapperFactory = TLSMemoryBIOFactory(clientContextFactory, True, clientFactory) sslClientProtocol = wrapperFactory.buildProtocol(None) serverProtocol = Protocol() serverFactory = ServerFactory() serverFactory.protocol = lambda: serverProtocol serverContextFactory = DefaultOpenSSLContextFactory(certPath, certPath) wrapperFactory = TLSMemoryBIOFactory(serverContextFactory, False, serverFactory) sslServerProtocol = wrapperFactory.buildProtocol(None) connectionDeferred = loopbackAsync(sslServerProtocol, sslClientProtocol) # Wait for the handshake before dropping the connection. def cbHandshake(ignored): serverProtocol.transport.loseConnection() # Now wait for the client to notice. return clientConnectionLost handshakeDeferred.addCallback(cbHandshake) # Wait for the connection to end, then make sure the client was # notified of a handshake failure. def cbConnectionDone(clientProtocol): clientProtocol.lostConnectionReason.trap(ConnectionDone) # The server should have closed its underlying transport, in # addition to whatever it did to shut down the TLS layer. self.assertTrue(serverProtocol.transport.q.disconnect) # The client should also have closed its underlying transport once # it saw the server shut down the TLS layer, so as to avoid relying # on the server to close the underlying connection. self.assertTrue(clientProtocol.transport.q.disconnect) handshakeDeferred.addCallback(cbConnectionDone) return handshakeDeferred
def test_data_cancels_timeout(self): """ When data is received, the timeout is canceled """ clock = Clock() protocol = HangCheckProtocol(Protocol(), reactor=clock) transport = StringTransport() transport.protocol = protocol protocol.makeConnection(transport) protocol.dataReceived('some-data') assert_clock_idle(self, clock)
def reconnector(self, onion): protocol = Protocol() protocol.onion = onion protocol.connectionLost = lambda failure: self.handleLostConnection( failure, onion) tor_endpoint = clientFromString(self.reactor, "tor:%s.onion:8060" % onion) self.onion_pending_map[onion] = connectProtocol(tor_endpoint, protocol) self.onion_pending_map[onion].addCallback( lambda protocol: self.connection_ready(onion, protocol)) self.onion_pending_map[onion].addErrback( lambda failure: self.connectFail(failure, onion))
def test_loseConnection(self): """ L{WebSocketsTransport.loseConnection} sends a close frame and closes the transport afterwards. """ transport = StringTransportWithDisconnection() transport.protocol = Protocol() webSocketsTranport = WebSocketsTransport(transport) webSocketsTranport.loseConnection() self.assertFalse(transport.connected) self.assertEqual(b"\x88\x02\x03\xe8", transport.value()) # We can call loseConnection again without side effects webSocketsTranport.loseConnection()
def test_spawnProcess(self): """ The process protocol for a spawned process is connected to a transport and appended onto the provided C{workers} list, and the reactor's C{spawnCount} increased. """ self.assertFalse(self.reactor.spawnCount) proto = Protocol() for count in [1, 2]: self.reactor.spawnProcess(proto, sys.executable, args=[sys.executable]) self.assertTrue(proto.transport) self.assertEqual(self.workers, [proto] * count) self.assertEqual(self.reactor.spawnCount, count)
def test_renderProtocol(self): """ If protocols are specified via the C{Sec-WebSocket-Protocol} header, L{WebSocketsResource} passes them to its C{lookupProtocol} argument, which can decide which protocol to return, and which is accepted. """ def lookupProtocol(names, otherRequest): self.assertEqual([b"foo", b"bar"], names) self.assertIs(request, otherRequest) return self.echoProtocol, b"bar" self.resource = WebSocketsResource(lookupProtocol) request = DummyRequest(b"/") request.requestHeaders = Headers( { b"sec-websocket-protocol": [b"foo", b"bar"], b"user-agent": [b"user-agent"], b"host": [b"host"], } ) transport = StringTransportWithDisconnection() transport.protocol = Protocol() request.transport = transport self.update_headers( request, headers={ b"upgrade": b"Websocket", b"connection": b"Upgrade", b"sec-websocket-key": b"secure", b"sec-websocket-version": b"13", }, ) result = self.resource.render(request) self.assertEqual(NOT_DONE_YET, result) self.assertEqual( { b"Connection": [b"Upgrade"], b"Upgrade": [b"WebSocket"], b"Sec-Websocket-Protocol": [b"bar"], b"Sec-Websocket-Accept": [b"oYBv54i42V5dw6KnZqOFroecUTc="], }, { name: value for name, value in request.responseHeaders.getAllRawHeaders() }, ) self.assertEqual([b""], request.written) self.assertEqual(101, request.responseCode)
def test_transport(self): """ The transport passed to the underlying protocol is the underlying transport. """ clock = Clock() wrapped_protocol = Protocol() protocol = HangCheckProtocol(wrapped_protocol, reactor=clock) transport = StringTransport() transport.protocol = protocol protocol.makeConnection(transport) self.assertIdentical(wrapped_protocol.transport, transport)
def test_allConnectionsGone_when_more_than_one_connection(self): """ When there are two connections allConnectionsGone fires when both connections go away. """ ctf = gracefulshutdown.ConnTrackingFactoryWrapper(Factory()) # Make two connection p1 = Protocol() p2 = Protocol() ctf.registerProtocol(p1) ctf.registerProtocol(p2) ctf.stopFactory() self.was_fired = False def cb(ignored): self.was_fired = True ctf.allConnectionsGone.addCallback(cb) self.assertFalse(self.was_fired) ctf.unregisterProtocol(p1) self.assertFalse(self.was_fired) ctf.unregisterProtocol(p2) self.assertTrue(self.was_fired)