def setUp(self): self.clock = Clock() self.temp_dir = FilePath(tempfile.mkdtemp()).asBytesMode() self.target = self.temp_dir.child(b'foo') with self.target.open('wb') as temp_fd: temp_fd.write(self.test_data) self.reader = DelayedReader(self.target, _clock=self.clock, delay=2) self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port)) self.rs = ReadSession(self.reader, _clock=self.clock) self.rs.transport = self.transport self.rs.startProtocol()
def setUp(self): self.clock = Clock() self.tmp_dir_path = tempfile.mkdtemp() self.target = FilePath(self.tmp_dir_path).child('foo') with self.target.open('wb') as temp_fd: temp_fd.write(self.test_data) self.reader = DelayedReader(self.target, _clock=self.clock, delay=2) self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port)) self.rs = ReadSession(self.reader, _clock=self.clock) self.rs.transport = self.transport self.rs.startProtocol()
class RemoteOriginReadSession(TFTPBootstrap): """Bootstraps a L{ReadSession}, that was started remotely, - we've received a RRQ. """ timeout = (1, 3, 7) def __init__(self, remote, reader, options=None, _clock=None): TFTPBootstrap.__init__(self, remote, reader, options, _clock) self.session = ReadSession(reader, self._clock) def startProtocol(self): """Start sending an OACK datagram if we were initialized with options or start the L{ReadSession} immediately. """ self.transport.connect(*self.remote) if self.options: self.resultant_options = self.processOptions(self.options) bytes = OACKDatagram(self.resultant_options).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, ) else: self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock() def _datagramReceived(self, datagram): if datagram.opcode == OP_ACK and datagram.blocknum == 0: return self.tftp_ACK(datagram) elif self.session.started: return self.session.datagramReceived(datagram) def tftp_ACK(self, datagram): """Handle incoming ACK datagram. Hand over control to the underlying L{ReadSession}. @param datagram: ACK datagram @type datagram: L{ACKDatagram} """ if self.timeout_watchdog is not None: self.timeout_watchdog.cancel() if not self.session.started: self.applyOptions(self.session, self.resultant_options) self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock()
class LocalOriginReadSession(TFTPBootstrap): """Bootstraps a L{ReadSession}, that was originated locally, - we've requested a write to a remote server. """ def __init__(self, remote, reader, options=None, _clock=None): TFTPBootstrap.__init__(self, remote, reader, options, _clock) self.session = ReadSession(reader, self._clock) def startProtocol(self): """Connect the transport and start the L{timeout_watchdog}""" self.transport.connect(*self.remote) if self.timeout_watchdog is not None: self.timeout_watchdog.start() def _datagramReceived(self, datagram): if datagram.opcode == OP_OACK: return self.tftp_OACK(datagram) elif (datagram.opcode == OP_ACK and datagram.blocknum == 0 and not self.session.started): self.session.transport = self.transport self.session.startProtocol() if self.timeout_watchdog is not None and self.timeout_watchdog.active( ): self.timeout_watchdog.cancel() return self.session.nextBlock() elif self.session.started: return self.session.datagramReceived(datagram) def tftp_OACK(self, datagram): """Handle incoming OACK datagram, process and apply options and hand over control to the underlying L{ReadSession}. @param datagram: OACK datagram @type datagram: L{OACKDatagram} """ if not self.session.started: self.resultant_options = self.processOptions(datagram.options) if self.timeout_watchdog is not None and self.timeout_watchdog.active( ): self.timeout_watchdog.cancel() self.applyOptions(self.session, self.resultant_options) self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock() else: log.msg("Duplicate OACK received, ignored")
class LocalOriginReadSession(TFTPBootstrap): """Bootstraps a L{ReadSession}, that was originated locally, - we've requested a write to a remote server. """ def __init__(self, remote, reader, options=None, _clock=None): TFTPBootstrap.__init__(self, remote, reader, options, _clock) self.session = ReadSession(reader, self._clock) def startProtocol(self): """Connect the transport and start the L{timeout_watchdog}""" self.transport.connect(*self.remote) if self.timeout_watchdog is not None: self.timeout_watchdog.start() def _datagramReceived(self, datagram): if datagram.opcode == OP_OACK: return self.tftp_OACK(datagram) elif (datagram.opcode == OP_ACK and datagram.blocknum == 0 and not self.session.started): self.session.transport = self.transport self.session.startProtocol() if self.timeout_watchdog is not None and self.timeout_watchdog.active(): self.timeout_watchdog.cancel() return self.session.nextBlock() elif self.session.started: return self.session.datagramReceived(datagram) def tftp_OACK(self, datagram): """Handle incoming OACK datagram, process and apply options and hand over control to the underlying L{ReadSession}. @param datagram: OACK datagram @type datagram: L{OACKDatagram} """ if not self.session.started: self.resultant_options = self.processOptions(datagram.options) if self.timeout_watchdog is not None and self.timeout_watchdog.active(): self.timeout_watchdog.cancel() self.applyOptions(self.session, self.resultant_options) self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock() else: log.msg("Duplicate OACK received, ignored")
class ReadSessions(unittest.TestCase): test_data = b"""line1 line2 anotherline""" port = 65466 def setUp(self): self.clock = Clock() self.temp_dir = FilePath(tempfile.mkdtemp()).asBytesMode() self.target = self.temp_dir.child(b'foo') with self.target.open('wb') as temp_fd: temp_fd.write(self.test_data) self.reader = DelayedReader(self.target, _clock=self.clock, delay=2) self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port)) self.rs = ReadSession(self.reader, _clock=self.clock) self.rs.transport = self.transport self.rs.startProtocol() @inlineCallbacks def test_ERROR(self): err_dgram = ERRORDatagram.from_code(ERR_NOT_DEFINED, b'no reason') yield self.rs.datagramReceived(err_dgram) self.assertFalse(self.transport.value()) self.assertTrue(self.transport.disconnecting) @inlineCallbacks 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) @inlineCallbacks def test_ACK_stale_blocknum(self): self.rs.blocknum = 2 ack_datagram = ACKDatagram(1) yield self.rs.datagramReceived(ack_datagram) self.assertFalse(self.transport.disconnecting) self.assertFalse( self.transport.value(), "Stale ACK datagram, we should not write anything back") self.addCleanup(self.rs.cancel) 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.assertFalse(self.transport.disconnecting) data_datagram = TFTPDatagramFactory( *split_opcode(self.transport.value())) self.assertEqual(data_datagram.data, b'line1') self.assertFalse( 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 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.assertTrue( 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 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 @inlineCallbacks 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.assertTrue(isinstance(err_datagram, ERRORDatagram)) self.assertTrue(self.transport.disconnecting) def test_rollover(self): self.rs.block_size = len(self.test_data) self.rs.blocknum = 65536 self.rs.dataFromReader(self.test_data) self.assertEqual(self.rs.blocknum, 0) self.addCleanup(self.rs.cancel) def tearDown(self): self.temp_dir.remove()
def __init__(self, remote, reader, options=None, _clock=None): TFTPBootstrap.__init__(self, remote, reader, options, _clock) self.session = ReadSession(reader, self._clock)
class RemoteOriginReadSession(TFTPBootstrap): """Bootstraps a L{ReadSession}, that was started remotely, - we've received a RRQ. """ timeout = (1, 3, 7) def __init__(self, remote, reader, options=None, _clock=None): TFTPBootstrap.__init__(self, remote, reader, options, _clock) self.session = ReadSession(reader, self._clock) def option_tsize(self, val): """Process tsize option. If tsize is zero, get the size of the file to be read so that it can be returned in the OACK datagram. @see: L{TFTPBootstrap.option_tsize} """ val = TFTPBootstrap.option_tsize(self, val) if val == b"0": val = self.session.reader.size if val is not None: val = intToBytes(val) return val def startProtocol(self): """Start sending an OACK datagram if we were initialized with options or start the L{ReadSession} immediately. """ self.transport.connect(*self.remote) if self.options: self.resultant_options = self.processOptions(self.options) bytes = OACKDatagram(self.resultant_options).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) else: self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock() def _datagramReceived(self, datagram): if datagram.opcode == OP_ACK and datagram.blocknum == 0 and self.session.started is False: return self.tftp_ACK(datagram) elif self.session.started: return self.session.datagramReceived(datagram) def tftp_ACK(self, datagram): """Handle incoming ACK datagram. Hand over control to the underlying L{ReadSession}. @param datagram: ACK datagram @type datagram: L{ACKDatagram} """ if self.timeout_watchdog is not None: self.timeout_watchdog.cancel() if not self.session.started: self.applyOptions(self.session, self.resultant_options) self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock()
class ReadSessions(unittest.TestCase): test_data = """line1 line2 anotherline""" port = 65466 def setUp(self): self.clock = Clock() self.tmp_dir_path = tempfile.mkdtemp() self.target = FilePath(self.tmp_dir_path).child('foo') with self.target.open('wb') as temp_fd: temp_fd.write(self.test_data) self.reader = DelayedReader(self.target, _clock=self.clock, delay=2) self.transport = FakeTransport(hostAddress=('127.0.0.1', self.port)) self.rs = ReadSession(self.reader, _clock=self.clock) self.rs.transport = self.transport self.rs.startProtocol() @inlineCallbacks def test_ERROR(self): err_dgram = ERRORDatagram.from_code(ERR_NOT_DEFINED, 'no reason') yield self.rs.datagramReceived(err_dgram) self.failIf(self.transport.value()) self.failUnless(self.transport.disconnecting) @inlineCallbacks def test_ACK_invalid_blocknum(self): ack_datagram = ACKDatagram(3) yield self.rs.datagramReceived(ack_datagram) self.failIf(self.transport.disconnecting) err_dgram = TFTPDatagramFactory(*split_opcode(self.transport.value())) self.assert_(isinstance(err_dgram, ERRORDatagram)) self.addCleanup(self.rs.cancel) @inlineCallbacks 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 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 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 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.failUnless(self.transport.disconnecting) d.addCallback(cb) self.clock.advance(2.5) return d @inlineCallbacks 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 tearDown(self): shutil.rmtree(self.tmp_dir_path)
class RemoteOriginReadSession(TFTPBootstrap): """Bootstraps a L{ReadSession}, that was started remotely, - we've received a RRQ. """ timeout = (1, 3, 7) def __init__(self, remote, reader, options=None, _clock=None): TFTPBootstrap.__init__(self, remote, reader, options, _clock) self.session = ReadSession(reader, self._clock) def option_tsize(self, val): """Process tsize option. If tsize is zero, get the size of the file to be read so that it can be returned in the OACK datagram. @see: L{TFTPBootstrap.option_tsize} """ val = TFTPBootstrap.option_tsize(self, val) if val == b"0": val = self.session.reader.size if val is not None: val = intToBytes(val) return val def startProtocol(self): """Start sending an OACK datagram if we were initialized with options or start the L{ReadSession} immediately. """ self.transport.connect(*self.remote) if self.options: self.resultant_options = self.processOptions(self.options) bytes = OACKDatagram(self.resultant_options).to_wire() self.timeout_watchdog = timedCaller( chain((0,), self.timeout), partial(self.transport.write, bytes), self.timedOut, clock=self._clock) else: self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock() def _datagramReceived(self, datagram): if datagram.opcode == OP_ACK and datagram.blocknum == 0 and self.session.started is False: return self.tftp_ACK(datagram) elif self.session.started: return self.session.datagramReceived(datagram) def tftp_ACK(self, datagram): """Handle incoming ACK datagram. Hand over control to the underlying L{ReadSession}. @param datagram: ACK datagram @type datagram: L{ACKDatagram} """ self.timeout_watchdog.cancel() if not self.session.started: self.applyOptions(self.session, self.resultant_options) self.session.transport = self.transport self.session.startProtocol() return self.session.nextBlock()