fix: register file-done channel before sending file-accept

For small files, file-done arrives before wireFileRecv's goroutine
reaches the pendingDones registration — the message was discarded and
the goroutine timed out. Fix by registering doneCh in AcceptOffer
(before sending file-accept), carrying it through recvState, and
reading it in wireFileRecv without re-registering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-07-02 18:57:06 +02:00
parent 153426367d
commit 6a3ad787d5

View File

@@ -777,13 +777,20 @@ func (s *Session) AcceptOffer(offer FileOffer, downloadDir string) {
if info, err := os.Stat(partPath); err == nil && info.Size() < offer.Size {
offset = info.Size()
}
s.controlSend(map[string]string{"type": "file-accept", "xid": offer.XID, "offset": fmt.Sprint(offset)})
// Register doneCh before sending file-accept so file-done can never arrive
// before the channel exists (small files finish before wireFileRecv runs).
doneCh := make(chan string, 1)
s.mu.Lock()
if s.pendingDones == nil {
s.pendingDones = make(map[string]chan string)
}
s.pendingDones[offer.XID] = doneCh
if s.pendingRecvs == nil {
s.pendingRecvs = make(map[string]*recvState)
}
s.pendingRecvs[offer.XID] = &recvState{offer: offer, dir: downloadDir, have: offset}
s.pendingRecvs[offer.XID] = &recvState{offer: offer, dir: downloadDir, have: offset, doneCh: doneCh}
s.mu.Unlock()
s.controlSend(map[string]string{"type": "file-accept", "xid": offer.XID, "offset": fmt.Sprint(offset)})
}
func (s *Session) RejectOffer(xid string) {
@@ -795,6 +802,7 @@ type recvState struct {
dir string
have int64
f *os.File
doneCh chan string
}
func (s *Session) wireFileRecv(dc *webrtc.DataChannel) {
@@ -885,13 +893,9 @@ func (s *Session) wireFileRecv(dc *webrtc.DataChannel) {
}
// §9: wait for file-done and verify sha256 before reporting success.
doneCh := make(chan string, 1)
s.mu.Lock()
if s.pendingDones == nil {
s.pendingDones = make(map[string]chan string)
}
s.pendingDones[xid] = doneCh
s.mu.Unlock()
// doneCh was registered in AcceptOffer before file-accept was sent,
// so file-done can never arrive before the channel exists.
doneCh := rs.doneCh
var expectedSHA256 string
select {
case expectedSHA256 = <-doneCh: