-
Notifications
You must be signed in to change notification settings - Fork 0
/
p2p.go
87 lines (73 loc) · 1.98 KB
/
p2p.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
79
80
81
82
83
84
85
86
87
package p2p
import (
"bytes"
"crypto/sha1"
"fmt"
"log"
"net"
"time"
"github.com/brianlusina/tclient/internal/client"
)
// MaxBlockSize is the largest number of bytes a request can ask for
const MaxBlockSize = 16384
// MaxBacklog is the number of unfulfilled requests a client can have in its pipeline
const MaxBacklog = 5
type pieceWork struct {
index int
hash [20]byte
length int
}
type pieceResult struct {
index int
buf []byte
}
func attemptDownloadPiece(c *client.Client, pw *pieceWork) ([]byte, error) {
state := pieceProgress{
index: pw.index,
client: c,
buf: make([]byte, pw.length),
}
// Setting a deadline helps get unresponsive peers unstuck.
// 30 seconds is more than enough time to download a 262 KB piece
if err := c.Conn.SetDeadline(time.Now().Add(30 * time.Second)); err != nil {
log.Fatalf("Failed to set connection deadline %s", err)
}
// defer c.Conn.SetDeadline(time.Time{}) // Disable the deadline
defer func(conn net.Conn) {
err := conn.SetDeadline(time.Time{}) // Disable the deadline
if err != nil {
log.Fatalf("An error occurred setting connection deadline")
return
}
}(c.Conn)
for state.downloaded < pw.length {
// If unchoked, send requests until we have enough unfulfilled requests
if !state.client.Choked {
for state.backlog < MaxBacklog && state.requested < pw.length {
blockSize := MaxBlockSize
// Last block might be shorter than the typical block
if pw.length-state.requested < blockSize {
blockSize = pw.length - state.requested
}
err := c.SendRequest(pw.index, state.requested, blockSize)
if err != nil {
return nil, err
}
state.backlog++
state.requested += blockSize
}
}
err := state.readMessage()
if err != nil {
return nil, err
}
}
return state.buf, nil
}
func checkIntegrity(pw *pieceWork, buf []byte) error {
hash := sha1.Sum(buf)
if !bytes.Equal(hash[:], pw.hash[:]) {
return fmt.Errorf("Index %d failed integrity check", pw.index)
}
return nil
}