def test_invalid_tid(self): bad_tid_dgram = ACKDatagram(123) yield self.ws.datagramReceived(bad_tid_dgram.to_wire(), ('127.0.0.1', 1111)) err_dgram = TFTPDatagramFactory(*split_opcode(self.transport.value())) self.assertEqual(err_dgram.errorcode, ERR_TID_UNKNOWN) self.addCleanup(self.ws.cancel)
def test_non_rq_datagram(self): tftp = TFTP(DummyBackend(), _clock=self.clock) tftp.transport = self.transport ack_datagram = ACKDatagram(14) tftp.datagramReceived(ack_datagram.to_wire(), ('127.0.0.1', 1111)) self.failIf(self.transport.disconnecting) self.failIf(self.transport.value())
def test_option_normal(self): self.ws.startProtocol() self.clock.advance(0.1) oack_datagram = OACKDatagram(self.options).to_wire() self.assertEqual(self.transport.value(), oack_datagram) self.clock.advance(3) self.assertEqual(self.transport.value(), oack_datagram * 2) self.transport.clear() self.ws.datagramReceived( DATADatagram(1, b'foobarbaz').to_wire(), ('127.0.0.1', 65465)) self.clock.pump((1, ) * 3) self.assertEqual(self.transport.value(), ACKDatagram(1).to_wire()) self.assertEqual(self.ws.session.block_size, 9) self.transport.clear() self.ws.datagramReceived( DATADatagram(2, b'smthng').to_wire(), ('127.0.0.1', 65465)) self.clock.pump((1, ) * 3) self.assertEqual(self.transport.value(), ACKDatagram(2).to_wire()) self.clock.pump((1, ) * 10) self.writer.finish() self.assertEqual( self.writer.file_path.open('r').read(), b'foobarbazsmthng') self.assertTrue(self.transport.disconnecting)
def cb(res): self.clock.advance(0.1) ack_datagram_1 = ACKDatagram(1) self.assertEqual(self.transport.value(), ack_datagram_1.to_wire()) self.assertEqual(self.target.open('r').read(), 'foobar') self.failIf(self.transport.disconnecting) self.addCleanup(self.ws.cancel)
def test_option_normal(self): self.ws.startProtocol() self.ws.datagramReceived(OACKDatagram({'blksize':'12'}).to_wire(), ('127.0.0.1', 65465)) self.clock.advance(0.1) self.assertEqual(self.ws.session.block_size, WriteSession.block_size) self.assertEqual(self.transport.value(), ACKDatagram(0).to_wire()) self.transport.clear() self.ws.datagramReceived(OACKDatagram({'blksize':'9'}).to_wire(), ('127.0.0.1', 65465)) self.clock.advance(0.1) self.assertEqual(self.ws.session.block_size, WriteSession.block_size) self.assertEqual(self.transport.value(), ACKDatagram(0).to_wire()) self.transport.clear() self.ws.datagramReceived(DATADatagram(1, 'foobarbaz').to_wire(), ('127.0.0.1', 65465)) self.clock.advance(3) self.failUnless(self.ws.session.started) self.clock.advance(0.1) self.assertEqual(self.ws.session.block_size, 9) self.assertEqual(self.transport.value(), ACKDatagram(1).to_wire()) self.transport.clear() self.ws.datagramReceived(DATADatagram(2, 'asdfghjkl').to_wire(), ('127.0.0.1', 65465)) self.clock.advance(3) self.assertEqual(self.transport.value(), ACKDatagram(2).to_wire()) self.writer.finish() self.assertEqual(self.writer.file_path.open('r').read(), 'foobarbazasdfghjkl') self.transport.clear() self.ws.datagramReceived(OACKDatagram({'blksize':'12'}).to_wire(), ('127.0.0.1', 65465)) self.clock.advance(0.1) self.assertEqual(self.ws.session.block_size, 9) self.assertEqual(self.transport.value(), ACKDatagram(0).to_wire())
def test_non_rq_datagram(self): tftp = TFTP(DummyBackend(), _clock=self.clock) tftp.transport = self.transport ack_datagram = ACKDatagram(14) tftp.datagramReceived(ack_datagram.to_wire(), ("127.0.0.1", 1111)) self.failIf(self.transport.disconnecting) self.failIf(self.transport.value())
def cb(res): self.clock.advance(0.1) ack_datagram_1 = ACKDatagram(1) self.assertEqual(self.transport.value(), ack_datagram_1.to_wire()) self.assertEqual(self.target.open('r').read(), 'foobar') self.failIf(self.transport.disconnecting) self.addCleanup(self.ws.cancel)
def test_invalid_tid(self): self.ws.startProtocol() bad_tid_dgram = ACKDatagram(123) self.ws.datagramReceived(bad_tid_dgram.to_wire(), ('127.0.0.1', 1111)) err_dgram = TFTPDatagramFactory(*split_opcode(self.transport.value())) self.assertEqual(err_dgram.errorcode, ERR_TID_UNKNOWN) self.addCleanup(self.ws.cancel)
def test_local_origin_read_session_handshake_success(self): self.clock.advance(1) ack_datagram = ACKDatagram(0) self.rs.datagramReceived(ack_datagram.to_wire(), ('127.0.0.1', 65465)) self.clock.advance(2) self.assertTrue(self.transport.value()) self.assertFalse(self.transport.disconnecting) self.addCleanup(self.rs.cancel)
def test_local_origin_read_session_handshake_success(self): self.clock.advance(1) ack_datagram = ACKDatagram(0) self.rs.datagramReceived(ack_datagram.to_wire(), ("127.0.0.1", 65465)) self.clock.advance(2) self.failUnless(self.transport.value()) self.failIf(self.transport.disconnecting) self.failIf(self.wd.active()) self.addCleanup(self.rs.cancel)
def test_ack(self): # Zero-length payload self.assertRaises(WireProtocolError, ACKDatagram.from_wire, '') # One byte payload self.assertRaises(WireProtocolError, ACKDatagram.from_wire, '\x00') # Full-length payload self.assertEqual(ACKDatagram.from_wire('\x00\x0a').blocknum, 10) self.assertEqual(ACKDatagram.from_wire('\x00\x0a').to_wire(), '\x00\x04\x00\x0a') # Extra data in payload self.assertRaises(WireProtocolError, ACKDatagram.from_wire, '\x00\x10foobarz')
def test_ack(self): # Zero-length payload self.assertRaises(WireProtocolError, ACKDatagram.from_wire, b'') # One byte payload self.assertRaises(WireProtocolError, ACKDatagram.from_wire, b'\x00') # Full-length payload self.assertEqual(ACKDatagram.from_wire(b'\x00\x0a').blocknum, 10) self.assertEqual( ACKDatagram.from_wire(b'\x00\x0a').to_wire(), b'\x00\x04\x00\x0a') # Extra data in payload self.assertRaises(WireProtocolError, ACKDatagram.from_wire, b'\x00\x10foobarz')
def tftp_OACK(self, datagram): """Handle the OACK datagram @param datagram: OACK datagram @type datagram: L{OACKDatagram} """ if not self.session.started: self.resultant_options = self.processOptions(datagram.options) self.timeout_watchdog.cancel() return self.transport.write(ACKDatagram(0).to_wire()) else: log.msg("Duplicate OACK received, send back ACK and ignore") self.transport.write(ACKDatagram(0).to_wire())
def test_ACK_backoff(self): self.rs.block_size = 5 self.rs.blocknum = 1 ack_datagram = ACKDatagram(1) d = self.rs.datagramReceived(ack_datagram) def cb(ign): self.clock.pump((1, ) * 4) # Sent two times - initial send and a retransmit after first timeout self.assertEqual(self.transport.value(), DATADatagram(2, self.test_data[:5]).to_wire() * 2) # Sent three times - initial send and two retransmits self.clock.pump((1, ) * 5) self.assertEqual(self.transport.value(), DATADatagram(2, self.test_data[:5]).to_wire() * 3) # Sent still three times - initial send, two retransmits and the last wait self.clock.pump((1, ) * 10) self.assertEqual(self.transport.value(), DATADatagram(2, self.test_data[:5]).to_wire() * 3) self.assertTrue(self.transport.disconnecting) d.addCallback(cb) self.clock.advance(2.5) return d
def test_option_normal(self): self.rs.startProtocol() self.rs.datagramReceived( OACKDatagram({ b'blksize': b'9' }).to_wire(), ('127.0.0.1', 65465)) self.clock.advance(0.1) self.assertEqual(self.rs.session.block_size, 9) self.clock.pump((1, ) * 3) self.assertEqual(self.transport.value(), DATADatagram(1, self.test_data[:9]).to_wire()) self.rs.datagramReceived( OACKDatagram({ b'blksize': b'12' }).to_wire(), ('127.0.0.1', 65465)) self.clock.advance(0.1) self.assertEqual(self.rs.session.block_size, 9) self.transport.clear() self.rs.datagramReceived( ACKDatagram(1).to_wire(), ('127.0.0.1', 65465)) self.clock.pump((1, ) * 3) self.assertEqual(self.transport.value(), DATADatagram(2, self.test_data[9:18]).to_wire()) self.addCleanup(self.rs.cancel)
def test_ACK_invalid_blocknum(self): ack_datagram = ACKDatagram(3) yield self.rs.datagramReceived(ack_datagram) self.assertFalse(self.transport.disconnecting) err_dgram = TFTPDatagramFactory(*split_opcode(self.transport.value())) self.assertTrue(isinstance(err_dgram, ERRORDatagram)) self.addCleanup(self.rs.cancel)
def test_ACK_stale_blocknum(self): self.rs.blocknum = 2 ack_datagram = ACKDatagram(1) yield self.rs.datagramReceived(ack_datagram) self.failIf(self.transport.disconnecting) self.failIf(self.transport.value(), "Stale ACK datagram, we should not write anything back") self.addCleanup(self.rs.cancel)
def cb(ign): self.clock.advance(0.1) ack_datagram = ACKDatagram(1) self.clock.pump((1,) * 5) # Sent two times - initial send and a retransmit after first timeout self.assertEqual(self.transport.value(), ack_datagram.to_wire() * 2) # Sent three times - initial send and two retransmits self.clock.pump((1,) * 4) self.assertEqual(self.transport.value(), ack_datagram.to_wire() * 3) # Sent still three times - initial send, two retransmits and the last wait self.clock.pump((1,) * 4) self.assertEqual(self.transport.value(), ack_datagram.to_wire() * 3) self.failUnless(self.transport.disconnecting)
def cb(ign): self.clock.advance(0.1) ack_datagram = ACKDatagram(2) # This datagram doesn't trigger any sends self.rs.datagramReceived(ack_datagram) self.assertEqual(self.transport.value(), DATADatagram(2, self.test_data).to_wire()) self.failUnless(self.rs.completed, "Data length is less, than blocksize, time to stop")
def test_local_origin_write_session_handshake_success(self): self.ws.session.block_size = 6 self.ws.startProtocol() self.clock.advance(1) data_datagram = DATADatagram(1, b'foobar') self.ws.datagramReceived(data_datagram.to_wire(), ('127.0.0.1', 65465)) self.clock.pump((1,)*3) self.assertEqual(self.transport.value(), ACKDatagram(1).to_wire()) self.assertFalse(self.transport.disconnecting) self.addCleanup(self.ws.cancel)
def test_remote_origin_write_bootstrap(self): # Initial ACK ack_datagram_0 = ACKDatagram(0) self.clock.advance(0.1) self.assertEqual(self.transport.value(), ack_datagram_0.to_wire()) self.failIf(self.transport.disconnecting) # Normal exchange self.transport.clear() d = self.ws.datagramReceived(DATADatagram(1, 'foobar').to_wire(), ('127.0.0.1', 65465)) def cb(res): self.clock.advance(0.1) ack_datagram_1 = ACKDatagram(1) self.assertEqual(self.transport.value(), ack_datagram_1.to_wire()) self.assertEqual(self.target.open('r').read(), 'foobar') self.failIf(self.transport.disconnecting) self.addCleanup(self.ws.cancel) d.addCallback(cb) self.clock.advance(3) return d
def cb(ign): self.clock.advance(0.1) ack_datagram = ACKDatagram(1) self.clock.pump((1, ) * 5) # Sent two times - initial send and a retransmit after first timeout self.assertEqual(self.transport.value(), ack_datagram.to_wire() * 2) # Sent three times - initial send and two retransmits self.clock.pump((1, ) * 4) self.assertEqual(self.transport.value(), ack_datagram.to_wire() * 3) # Sent still three times - initial send, two retransmits and the last wait self.clock.pump((1, ) * 4) self.assertEqual(self.transport.value(), ack_datagram.to_wire() * 3) self.assertTrue(self.transport.disconnecting)
def test_remote_origin_write_bootstrap(self): # Initial ACK ack_datagram_0 = ACKDatagram(0) self.clock.advance(0.1) self.assertEqual(self.transport.value(), ack_datagram_0.to_wire()) self.failIf(self.transport.disconnecting) # Normal exchange self.transport.clear() d = self.ws.datagramReceived(DATADatagram(1, 'foobar').to_wire(), ('127.0.0.1', 65465)) def cb(res): self.clock.advance(0.1) ack_datagram_1 = ACKDatagram(1) self.assertEqual(self.transport.value(), ack_datagram_1.to_wire()) self.assertEqual(self.target.open('r').read(), 'foobar') self.failIf(self.transport.disconnecting) self.addCleanup(self.ws.cancel) d.addCallback(cb) self.clock.advance(3) return d
def test_failed_read(self): self.reader.finish() self.rs.reader = FailingReader() self.rs.blocknum = 1 ack_datagram = ACKDatagram(1) yield self.rs.datagramReceived(ack_datagram) self.flushLoggedErrors() self.clock.advance(0.1) err_datagram = TFTPDatagramFactory(*split_opcode(self.transport.value())) self.failUnless(isinstance(err_datagram, ERRORDatagram)) self.failUnless(self.transport.disconnecting)
def test_option_normal(self): self.rs.startProtocol() self.clock.advance(0.1) oack_datagram = OACKDatagram(self.options).to_wire() self.assertEqual(self.transport.value(), oack_datagram) self.clock.advance(3) self.assertEqual(self.transport.value(), oack_datagram * 2) self.transport.clear() self.rs.datagramReceived(ACKDatagram(0).to_wire(), ('127.0.0.1', 65465)) self.clock.pump((1,)*3) self.assertEqual(self.transport.value(), DATADatagram(1, self.test_data[:9]).to_wire()) self.addCleanup(self.rs.cancel)
def startProtocol(self): """Connect the transport, respond with an initial ACK or OACK (depending on if we were initialized with options or not). """ self.transport.connect(*self.remote) if self.options: self.resultant_options = self.processOptions(self.options) bytes = OACKDatagram(self.resultant_options).to_wire() else: bytes = ACKDatagram(0).to_wire() self.timeout_watchdog = timedCaller(chain((0, ), self.timeout), partial(self.transport.write, bytes), self.timedOut, clock=self._clock)
def test_ACK(self): self.rs.block_size = 5 self.rs.blocknum = 1 ack_datagram = ACKDatagram(1) d = self.rs.datagramReceived(ack_datagram) def cb(ign): self.clock.advance(0.1) self.failIf(self.transport.disconnecting) data_datagram = TFTPDatagramFactory(*split_opcode(self.transport.value())) self.assertEqual(data_datagram.data, 'line1') self.failIf(self.rs.completed, "Got enough bytes from the reader, there is no reason to stop") d.addCallback(cb) self.clock.advance(2.5) self.addCleanup(self.rs.cancel) return d
def startProtocol(self): """Connect the transport, respond with an initial ACK or OACK (depending on if we were initialized with options or not). """ self.transport.connect(*self.remote) if self.options: self.resultant_options = self.processOptions(self.options) bytes = OACKDatagram(self.resultant_options).to_wire() else: bytes = ACKDatagram(0).to_wire() self.timeout_watchdog = SequentialCall.run( self.timeout[:-1], callable=self.transport.write, callable_args=[bytes, ], on_timeout=lambda: self._clock.callLater(self.timeout[-1], self.timedOut), run_now=True, _clock=self._clock )
def tftp_DATA(self, datagram): """Handle incoming DATA TFTP datagram @type datagram: L{DATADatagram} """ next_blocknum = self.blocknum + 1 if datagram.blocknum < next_blocknum: self.transport.write(ACKDatagram(datagram.blocknum).to_wire()) elif datagram.blocknum == next_blocknum: if self.completed: self.transport.write(ERRORDatagram.from_code( ERR_ILLEGAL_OP, "Transfer already finished").to_wire()) else: return self.nextBlock(datagram) else: self.transport.write(ERRORDatagram.from_code( ERR_ILLEGAL_OP, "Block number mismatch").to_wire())
def blockWriteSuccess(self, ign, datagram): """The write was successful, respond with ACK for current block number If this is the last chunk (received data length < block size), the protocol will keep running until the end of current timeout period, so we can respond to any duplicates. @type datagram: L{DATADatagram} """ bytes = ACKDatagram(datagram.blocknum).to_wire() self.timeout_watchdog = timedCaller((0, ) + self.timeout, partial(self.sendData, bytes), self.timedOut, clock=self._clock) if len(datagram.data) < self.block_size: self.completed = True self.writer.finish()
def test_remote_origin_read_bootstrap(self): # First datagram self.rs.session.block_size = 5 self.rs.startProtocol() self.clock.pump((1,)*3) data_datagram_1 = DATADatagram(1, self.test_data[:5]) self.assertEqual(self.transport.value(), data_datagram_1.to_wire()) self.failIf(self.transport.disconnecting) # Normal exchange continues self.transport.clear() self.rs.datagramReceived(ACKDatagram(1).to_wire(), ('127.0.0.1', 65465)) self.clock.pump((1,)*3) data_datagram_2 = DATADatagram(2, self.test_data[5:10]) self.assertEqual(self.transport.value(), data_datagram_2.to_wire()) self.failIf(self.transport.disconnecting) self.addCleanup(self.rs.cancel)
def blockWriteSuccess(self, ign, datagram): """The write was successful, respond with ACK for current block number If this is the last chunk (received data length < block size), the protocol will keep running until the end of current timeout period, so we can respond to any duplicates. @type datagram: L{DATADatagram} """ bytes = ACKDatagram(datagram.blocknum).to_wire() self.timeout_watchdog = SequentialCall.run(self.timeout[:-1], callable=self.sendData, callable_args=[bytes, ], on_timeout=lambda: self._clock.callLater(self.timeout[-1], self.timedOut), run_now=True, _clock=self._clock ) if len(datagram.data) < self.block_size: self.completed = True self.writer.finish()
def test_ACK_finished(self): self.rs.block_size = 512 self.rs.blocknum = 1 # Send a terminating datagram ack_datagram = ACKDatagram(1) d = self.rs.datagramReceived(ack_datagram) def cb(ign): self.clock.advance(0.1) ack_datagram = ACKDatagram(2) # This datagram doesn't trigger any sends self.rs.datagramReceived(ack_datagram) self.assertEqual(self.transport.value(), DATADatagram(2, self.test_data).to_wire()) self.failUnless(self.rs.completed, "Data length is less, than blocksize, time to stop") self.addCleanup(self.rs.cancel) d.addCallback(cb) self.clock.advance(3) return d
def blockWriteSuccess(self, ign, datagram): """The write was successful, respond with ACK for current block number If this is the last chunk (received data length < block size), the protocol will keep running until the end of current timeout period, so we can respond to any duplicates. @type datagram: L{DATADatagram} """ bytes = ACKDatagram(datagram.blocknum).to_wire() if len(datagram.data) < self.block_size: self._clock.callLater(0, self.sendData, bytes) self.timeout_watchdog = SequentialCall.run( self.timeout[:-1], callable=lambda: None, on_timeout=lambda: self._clock.callLater( self.timeout[-1], self.timedOut), run_now=False, _clock=self._clock) self.completed = True self.writer.finish() # TODO: If self.tsize is not None, compare it with the actual # count of bytes written. Log if there's a mismatch. Should it # also emit an error datagram? else: self.timeout_watchdog = SequentialCall.run( self.timeout[:-1], callable=self.sendData, callable_args=[ bytes, ], on_timeout=lambda: self._clock.callLater( self.timeout[-1], self.timedOut), run_now=True, _clock=self._clock)