mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-24 16:41:30 +02:00
The receiver reported progress once per file, after the whole body had been staged: spool.Write drains its reader before returning, so a large file sat at nothing until it jumped to done. It now stages through a reader that reports as the bytes land, the same way the sender already did, and both sides thin their reports to one per percent and one per 200ms so a fast transfer cannot flood the history or the UI. Progress also becomes an event of its own. It was left out because a report per byte would have been unusable; thinned, it costs a handful of events a second and spares every UI a poll. The daemon's event bridge ignores the new kind, so nothing is published where a notification would be noise. Withdrawing consent mid-transfer did nothing. Cancel routed through Decide, which only moves an offer out of Pending, so an accepted offer kept its decision, kept its spool, and kept being uploaded into: the receiver's list went quiet while the sender ran to completion. Revoke takes an offer back whatever it has already answered, the staging reader gives up as soon as consent is gone, and the sender stops rather than retrying a refusal three times over. A transfer withdrawn this way reads as declined on both ends, which is what it is, rather than as a failure.
58 lines
1.3 KiB
Go
58 lines
1.3 KiB
Go
package filedrop
|
|
|
|
import (
|
|
"io"
|
|
"time"
|
|
)
|
|
|
|
// progressInterval is the floor between two progress reports of one item. A
|
|
// percent of a large file can pass in a millisecond, and every report ends up
|
|
// writing history and waking the UI, so the rate is capped regardless.
|
|
const progressInterval = 200 * time.Millisecond
|
|
|
|
// progressReader reports how much of an item has moved as it is read. Reports
|
|
// are thinned to one per whole percent, and to one per progressInterval when
|
|
// the bytes outrun even that; the last one always goes through so a transfer
|
|
// never stops a tick short of done.
|
|
type progressReader struct {
|
|
r io.Reader
|
|
sent int64
|
|
total int64
|
|
report func(sent int64)
|
|
percent int
|
|
last time.Time
|
|
}
|
|
|
|
func (p *progressReader) Read(b []byte) (int, error) {
|
|
n, err := p.r.Read(b)
|
|
if n == 0 {
|
|
return n, err
|
|
}
|
|
|
|
p.sent += int64(n)
|
|
if p.due(time.Now()) {
|
|
p.report(p.sent)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// due decides whether the reader has moved enough, and waited long enough, to
|
|
// be worth another report.
|
|
func (p *progressReader) due(now time.Time) bool {
|
|
if p.total <= 0 || p.sent >= p.total {
|
|
return true
|
|
}
|
|
|
|
percent := int(p.sent * 100 / p.total)
|
|
if percent == p.percent {
|
|
return false
|
|
}
|
|
if !p.last.IsZero() && now.Sub(p.last) < progressInterval {
|
|
return false
|
|
}
|
|
|
|
p.percent = percent
|
|
p.last = now
|
|
return true
|
|
}
|