You've already forked torrent-client
mirror of
https://github.com/veggiedefender/torrent-client.git
synced 2025-11-06 09:29:16 +02:00
Break p2p.Peer and parsePeers into its own package
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/veggiedefender/torrent-client/bitfield"
|
||||
"github.com/veggiedefender/torrent-client/peers"
|
||||
|
||||
"github.com/veggiedefender/torrent-client/message"
|
||||
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type client struct {
|
||||
peer Peer
|
||||
peer peers.Peer
|
||||
infoHash [20]byte
|
||||
peerID [20]byte
|
||||
conn net.Conn
|
||||
@@ -57,7 +58,7 @@ func recvBitfield(conn net.Conn, r *bufio.Reader) (bitfield.Bitfield, error) {
|
||||
return msg.Payload, nil
|
||||
}
|
||||
|
||||
func newClient(peer Peer, peerID, infoHash [20]byte) (*client, error) {
|
||||
func newClient(peer peers.Peer, peerID, infoHash [20]byte) (*client, error) {
|
||||
hostPort := net.JoinHostPort(peer.IP.String(), strconv.Itoa(int(peer.Port)))
|
||||
conn, err := net.DialTimeout("tcp", hostPort, 3*time.Second)
|
||||
if err != nil {
|
||||
|
||||
12
p2p/p2p.go
12
p2p/p2p.go
@@ -5,11 +5,11 @@ import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/veggiedefender/torrent-client/message"
|
||||
"github.com/veggiedefender/torrent-client/peers"
|
||||
)
|
||||
|
||||
// MaxBlockSize is the largest number of bytes a request can ask for
|
||||
@@ -18,15 +18,9 @@ const MaxBlockSize = 16384
|
||||
// MaxBacklog is the number of unfulfilled requests a client can have in its pipeline
|
||||
const MaxBacklog = 5
|
||||
|
||||
// Peer encodes connection information for a peer
|
||||
type Peer struct {
|
||||
IP net.IP
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// Torrent holds data required to download a torrent from a list of peers
|
||||
type Torrent struct {
|
||||
Peers []Peer
|
||||
Peers []peers.Peer
|
||||
PeerID [20]byte
|
||||
InfoHash [20]byte
|
||||
PieceHashes [][20]byte
|
||||
@@ -146,7 +140,7 @@ func checkIntegrity(pw *pieceWork, buf []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Torrent) startDownloadWorker(peer Peer, workQueue chan *pieceWork, results chan *pieceResult) {
|
||||
func (t *Torrent) startDownloadWorker(peer peers.Peer, workQueue chan *pieceWork, results chan *pieceResult) {
|
||||
c, err := newClient(peer, t.PeerID, t.InfoHash)
|
||||
if err != nil {
|
||||
log.Printf("Could not handshake with %s. Disconnecting\n", peer.IP)
|
||||
|
||||
30
peers/peers.go
Normal file
30
peers/peers.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package peers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Peer encodes connection information for a peer
|
||||
type Peer struct {
|
||||
IP net.IP
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// Unmarshal parses peer IP addresses and ports from a buffer
|
||||
func Unmarshal(peersBin []byte) ([]Peer, error) {
|
||||
const peerSize = 6 // 4 for IP, 2 for port
|
||||
numPeers := len(peersBin) / peerSize
|
||||
if len(peersBin)%peerSize != 0 {
|
||||
err := fmt.Errorf("Received malformed peers")
|
||||
return nil, err
|
||||
}
|
||||
peers := make([]Peer, numPeers)
|
||||
for i := 0; i < numPeers; i++ {
|
||||
offset := i * peerSize
|
||||
peers[i].IP = net.IP(peersBin[offset : offset+4])
|
||||
peers[i].Port = binary.BigEndian.Uint16([]byte(peersBin[offset+4 : offset+6]))
|
||||
}
|
||||
return peers, nil
|
||||
}
|
||||
39
peers/peers_test.go
Normal file
39
peers/peers_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package peers
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
output []Peer
|
||||
fails bool
|
||||
}{
|
||||
"correctly parses peers": {
|
||||
input: string([]byte{127, 0, 0, 1, 0x00, 0x50, 1, 1, 1, 1, 0x01, 0xbb}),
|
||||
output: []Peer{
|
||||
{IP: net.IP{127, 0, 0, 1}, Port: 80},
|
||||
{IP: net.IP{1, 1, 1, 1}, Port: 443},
|
||||
},
|
||||
},
|
||||
"not enough bytes in peers": {
|
||||
input: string([]byte{127, 0, 0, 1, 0x00}),
|
||||
output: nil,
|
||||
fails: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
peers, err := Unmarshal([]byte(test.input))
|
||||
if test.fails {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
assert.Equal(t, test.output, peers)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
package torrentfile
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/veggiedefender/torrent-client/peers"
|
||||
|
||||
"github.com/jackpal/bencode-go"
|
||||
"github.com/veggiedefender/torrent-client/p2p"
|
||||
)
|
||||
|
||||
type bencodeTrackerResp struct {
|
||||
@@ -18,22 +16,6 @@ type bencodeTrackerResp struct {
|
||||
Peers string `bencode:"port"`
|
||||
}
|
||||
|
||||
func parsePeers(peersBin string) ([]p2p.Peer, error) {
|
||||
const peerSize = 6 // 4 for IP, 2 for port
|
||||
numPeers := len(peersBin) / peerSize
|
||||
if len(peersBin)%peerSize != 0 {
|
||||
err := fmt.Errorf("Received malformed peers")
|
||||
return nil, err
|
||||
}
|
||||
peers := make([]p2p.Peer, numPeers)
|
||||
for i := 0; i < numPeers; i++ {
|
||||
offset := i * peerSize
|
||||
peers[i].IP = net.IP(peersBin[offset : offset+4])
|
||||
peers[i].Port = binary.BigEndian.Uint16([]byte(peersBin[offset+4 : offset+6]))
|
||||
}
|
||||
return peers, nil
|
||||
}
|
||||
|
||||
func (t *TorrentFile) buildTrackerURL(peerID [20]byte, port uint16) (string, error) {
|
||||
base, err := url.Parse(t.Announce)
|
||||
if err != nil {
|
||||
@@ -52,7 +34,7 @@ func (t *TorrentFile) buildTrackerURL(peerID [20]byte, port uint16) (string, err
|
||||
return base.String(), nil
|
||||
}
|
||||
|
||||
func (t *TorrentFile) requestPeers(peerID [20]byte, port uint16) ([]p2p.Peer, error) {
|
||||
func (t *TorrentFile) requestPeers(peerID [20]byte, port uint16) ([]peers.Peer, error) {
|
||||
url, err := t.buildTrackerURL(peerID, port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -71,7 +53,7 @@ func (t *TorrentFile) requestPeers(peerID [20]byte, port uint16) ([]p2p.Peer, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peers, err := parsePeers(trackerResp.Peers)
|
||||
peers, err := peers.Unmarshal([]byte(trackerResp.Peers))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package torrentfile
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/veggiedefender/torrent-client/p2p"
|
||||
)
|
||||
|
||||
func TestBuildTrackerURL(t *testing.T) {
|
||||
@@ -27,34 +25,3 @@ func TestBuildTrackerURL(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, url, expected)
|
||||
}
|
||||
|
||||
func TestParsePeers(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
output []p2p.Peer
|
||||
fails bool
|
||||
}{
|
||||
"correctly parses peers": {
|
||||
input: string([]byte{127, 0, 0, 1, 0x00, 0x50, 1, 1, 1, 1, 0x01, 0xbb}),
|
||||
output: []p2p.Peer{
|
||||
{IP: net.IP{127, 0, 0, 1}, Port: 80},
|
||||
{IP: net.IP{1, 1, 1, 1}, Port: 443},
|
||||
},
|
||||
},
|
||||
"not enough bytes in peers": {
|
||||
input: string([]byte{127, 0, 0, 1, 0x00}),
|
||||
output: nil,
|
||||
fails: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
peers, err := parsePeers(test.input)
|
||||
if test.fails {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
assert.Equal(t, test.output, peers)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user