tftp.transfer
tftp.transfer
The transfer engine: one side of a DATA/ACK exchange, without any I/O.
:class:Sender produces DATA and consumes ACKs; :class:Receiver consumes
DATA and produces ACKs. :class:Requester is the client's opening: it sends
the request, repeats it and judges the first answer. None touches a socket or
a clock: packets go out through a send callable, packets come in through
:meth:Transfer.handle (:meth:Requester.handle), and the driver calls
on_timeout once deadline passes. The client and the server drive the
same classes, and the tests drive them over a simulated lossy link.
Behaviour worth knowing:
- Windowing (RFC 7440). A sender keeps a whole window of DATA in memory, so a retransmission never re-reads the source. An ACK inside the window restarts sending right after the acknowledged block.
- Sorcerer's Apprentice (RFC 1123 4.2.3.1). A duplicate ACK never
triggers a retransmission when
windowsizeis 1; only a timeout does. With a larger window the first duplicate of a given ACK resends the window (it is how a receiver reports a lost block) and later duplicates are ignored. - Block number rollover. Block numbers are tracked as unbounded integers
and mapped to the 16-bit wire value, wrapping after 65535 to
rollover(0 by default, 1 when negotiated), so file size is unlimited.
Receiver(send, write, negotiated, retries, now, reply=None, complete=None, *, backoff=2.0, max_timeout=None, expires=None, max_idle=None)
Bases: Transfer
Writes DATA packets to a sink and acknowledges them.
:param write: from :func:as_write. It may raise :class:WouldBlock:
the block is then held unacknowledged until :meth:resume writes it,
which is backpressure the sender sees as a slower ACK.
:param reply: sent first and resent on timeout until DATA 1 arrives --
ACK 0 for a server answering a WRQ (or a client acknowledging an
OACK), the OACK for a server answering a WRQ with options. None
when the first DATA is about to be handed in (a client whose RRQ was
answered with DATA 1 directly).
:param complete: called after the last DATA is written and before it is
acknowledged; if it raises, the peer gets an ERROR instead of the
final ACK (a server committing an upload to disk). It may raise
:class:WouldBlock too, holding the final ACK.
After the final ACK, is_done is set and the receiver keeps answering a
repeated final DATA with that ACK, so a driver can "dally" (RFC 1350
section 6) by feeding it packets for a while longer.
Keyword arguments are :class:Transfer's (backoff, max_timeout,
expires).
Source code in src/tftp/transfer/_receiver.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | |
Requester(send, server, opcode, filename, retries, now, *, mode='octet', options=None, timeout=1.0, backoff=2.0, max_timeout=None, expires=None, registry=None, accepts=None, fallback=True, probe=False)
Sends a request, repeats it until the first answer arrives and judges that answer.
The request goes out from the constructor. The driver feeds it every
datagram that arrives (:meth:handle) and calls :meth:on_timeout once
deadline has passed; when is_done is set, error says whether the
exchange failed and the outcome attributes say what the answer was. It
reads no clock and touches no socket.
:param send: send(packet, address): the request goes to server, a
refusal of the answer to the address it came from. May raise OSError:
the outcome is recorded first, then the error propagates.
:param server: where the request goes: (host, port, ...), opaque but
for its first two items in a message.
:param opcode: RRQ or WRQ.
:param retries: repetitions of the request before giving up.
:param now: the driver's clock when the request is sent.
:param mode: "octet" or "netascii".
:param options: the options the request carries, verbatim (None for none).
:param timeout: seconds before the first repeat, and the timeout of a
transfer that runs on the defaults.
:param backoff: each consecutive repeat waits this many times longer.
:param max_timeout: ceiling for the backed-off wait; None is eight
times timeout.
:param expires: the instant, in the driver's clock, after which the
exchange fails whatever retries says, or None.
:param registry: the :class:OptionRegistry an OACK is validated with.
:param accepts: accepts(address): whether a datagram from address may
be the answer. One that may not is dropped without a reply. None
takes the first datagram from anywhere.
:param fallback: allow :attr:retry_without_options.
:param probe: the exchange only learns what the server answers: the answer
is declined (ERROR 8 to an OACK, ACK 1 or ERROR 0 to DATA 1) and
:attr:negotiated stays None.
:ivar is_done: the exchange has ended, in an answer or in a failure.
:ivar error: the failure, or None: the :class:RemoteError of the
server's code, :class:TFTPProtocolError for an answer no server may
send or an OACK that cannot be accepted, :class:TransferTimeoutError.
:ivar deadline: when :meth:on_timeout is due, in the driver's clock;
None once the exchange has ended.
:ivar peer: the address of the answer, or None before one.
:ivar negotiated: what the answer settles (:class:Negotiated), or None
on a failure and in a probe.
:ivar first_data: the DATA 1 datagram when it was the answer to a read, else None.
:ivar oack: the options of the OACK the server sent, as sent, or None.
:ivar options: the options this request carried.
:ivar retry_without_options: the answer was ERROR 8, 4 or 0 to a request that
carried options and fallback is on: the request may be made again,
by a new :class:Requester with options={}.
Source code in src/tftp/transfer/_requester.py
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | |
handle(packet, n, address, now)
Take a datagram: it is the first answer, or it is dropped and nothing is sent.
Source code in src/tftp/transfer/_requester.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | |
on_timeout(now)
Repeat the request, or fail when the time limit or the retries are spent.
Source code in src/tftp/transfer/_requester.py
172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
Sender(send, read, negotiated, retries, now, oack=None, *, backoff=2.0, max_timeout=None, expires=None, max_idle=None)
Bases: Transfer
Sends a stream as DATA packets: a server's RRQ, a client's WRQ.
:param read: a fill-the-view callable from :func:as_readinto. It may
raise :class:WouldBlock; sending then pauses until :meth:resume.
:param oack: when given, sent first, and DATA starts after ACK 0 (a
server answering an RRQ that carried options). Otherwise DATA starts
at once.
Keyword arguments are :class:Transfer's (backoff, max_timeout,
expires).
Source code in src/tftp/transfer/_sender.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | |
SupportsRead
Bases: Protocol
A source with read(size) returning bytes (None: not ready).
SupportsReadinto
Bases: Protocol
A source with readinto(buffer), filling the buffer and returning the count (None: not ready).
SupportsWrite
Bases: Protocol
A sink with write(data): a binary file, or any object that takes bytes.
Transfer(send, negotiated, retries, *, backoff=2.0, max_timeout=None, expires=None, max_idle=None)
State shared by both directions.
:param retries: retransmissions of one packet before giving up.
:param backoff: each consecutive retransmission waits this much longer
(RFC 1123 4.2.3.2 asks for at least exponential backoff); progress
resets the wait to the negotiated timeout.
:param max_timeout: ceiling for the backed-off wait; None is eight
times the timeout.
:param expires: absolute time (driver's clock) by which the transfer must
finish, or None.
:param max_idle: seconds with no datagram from the peer after which the
transfer fails, whatever timeout was negotiated; None is no bound.
Time spent waiting on the local source or sink does not count.
:ivar is_done: the transfer is finished, successfully or not.
:ivar error: the failure, or None.
:ivar deadline: when :meth:on_timeout is due, in the driver's clock;
None while nothing is outstanding.
:ivar is_stalled: waiting for the source or sink (see :class:WouldBlock).
:ivar bytes: payload bytes moved so far.
:ivar retransmits: packets sent again after loss.
Source code in src/tftp/transfer/_engine.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | |
abort(message='transfer aborted')
Cancel locally: the peer gets ERROR 0 and error is :class:TransferAbortedError.
Source code in src/tftp/transfer/_engine.py
250 251 252 | |
fail(exc, notify=True)
End the transfer with exc, sending the peer an ERROR if asked.
Source code in src/tftp/transfer/_engine.py
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | |
resume(now)
Retry what :class:WouldBlock interrupted.
Source code in src/tftp/transfer/_engine.py
267 268 269 | |
as_readinto(source)
A readinto-style callable for source, filling the view fully.
Short reads are retried until the view is full or the source is
exhausted, so a short return means end of data. source needs
readinto or read; either returning None is "nothing ready"
and raises :class:WouldBlock.
Source code in src/tftp/transfer/_engine.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | |
as_write(sink)
A write callable that is safe to hand a reused receive buffer.
Standard file objects copy what they are given, so they get the buffer
directly. Anything else gets bytes, because an object that kept a
reference to the buffer would see it overwritten by the next packet.
A buffered file takes the whole block. For any other sink a count below the
block's length is a short write (io.RawIOBase.write): the rest is
written until it is taken, and a sink that takes nothing raises
OSError. A raw sink that returns None has nothing ready:
:class:WouldBlock. Any other sink's None is "all of it".
Source code in src/tftp/transfer/_engine.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |