feat: http downloader

This commit is contained in:
liwei committed 2020-08-03 18:35:50 +08:00
1 parent 15e5d2af3b
commit 788376eaa9
22 files changed
+813 -64

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
package dht
import (
"github.com/marksamman/bencode"
"net"
)
func ping() string {
data := map[string]interface{}{
"t": "aa",
"y": "q",
"q": "ping",
"a": map[string]interface{}{"id": "abcdefghij0123456789"},
}
req := bencode.Encode(data)
// session.add_dht_router("router.utorrent.com", 6881)
// session.add_dht_router("router.bittorrent.com", 6881)
// session.add_dht_router("dht.transmissionbt.com", 6881)
// session.add_dht_router("router.bitcomet.com", 6881)
// session.add_dht_router("dht.aelitis.com", 6881)
dial, err := net.Dial("udp", "router.utorrent.com:6881")
if err != nil {
panic(err)
}
dial.Write(req)
reps := make([]byte, 65535)
n, err := dial.Read(reps)
if err != nil {
panic(err)
}
return string(reps[:n])
}
+7
View File
@@ -0,0 +1,7 @@
package dht
import "testing"
func Test_ping(t *testing.T) {
println(ping())
}
+4 -15
View File
@@ -3,31 +3,20 @@ package metainfo
import (
"encoding/hex"
"fmt"
"github.com/RoaringBitmap/roaring"
"testing"
)
func TestParseFromFile(t *testing.T) {
// metaInfo, err := ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent")
metaInfo, err := ParseFromFile("../testdata/office.torrent")
metaInfo, err := ParseFromFile("E:\\bt\\win10.torrent")
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", metaInfo.AnnounceList)
fmt.Printf("%+v\n", metaInfo.Info.Files)
for _, file := range metaInfo.Info.Files {
fmt.Printf("%+v\n", file)
}
fmt.Printf("%+v\n", metaInfo.Info.PieceLength)
fmt.Printf("%+v\n", len(metaInfo.Info.Pieces))
fmt.Printf("%+v\n", hex.EncodeToString(metaInfo.infoHash[:]))
}
func TestParseFromFile2(t *testing.T) {
a := 0b10010000
b := 0b01111111
fmt.Printf("%08b\n", (a&b)^b)
have := roaring.BitmapOf(0, 3)
peer := roaring.BitmapOf(1, 2, 3, 4, 5, 6, 7)
need := roaring.Xor(roaring.And(have, peer), peer)
fmt.Println(need.ToArray())
}
+3 -4
View File
@@ -60,10 +60,9 @@ func (b *Bitfield) IsComplete(i int) bool {
}
// 给定一组分片下载状态,计算出当前peer能提供下载的分片下标,如果已经拥有则不用提供
// had = 0b10010000,has = 0b01111111
// had & has = 0b10010000 & 0b01111111 = 0b00010000
// had & has ^ has = 0b00010000 ^ 0b01111111 = 0b01101111
// has = 0b01111111,had = 0b10010000
// has & ~had = 0b01111111 & 0b01101111 = 0b01101111
func (b *Bitfield) Provide(had *roaring.Bitmap) []uint32 {
xor := roaring.Xor(roaring.And(had, b.bitmap), b.bitmap)
xor := roaring.AndNot(b.bitmap, had)
return xor.ToArray()
}
+2 -1
View File
@@ -324,7 +324,7 @@ func (pc *peerConn) handlePiece(buf []byte) {
fileWritable := f.End - blockFileBegin
// 计算block剩余写入字节数
blockWritable := blockLength - blockSeek
fb := fileBlock{
fb := &fileBlock{
filepath: filepath.Join(f.Path...),
fileSeek: blockFileBegin - f.Begin,
blockBegin: blockSeek,
@@ -420,6 +420,7 @@ func (pc *peerConn) handlePiece(buf []byte) {
}
// 获取piece对应要写入的文件
// TODO 使用sort.Search二分查找法检索
func getWriteFile(pieceBegin int64, fds []*metainfo.FileDetail) int {
for i, f := range fds {
if f.Begin <= pieceBegin && f.End > pieceBegin {
+1 -1
View File
@@ -64,7 +64,7 @@ func getUsablePeerMore() chan *peerConn {
peers := <-tracker.Tracker()
ch := make(chan *peerConn)
for i := range peers {
go peerTest(torrent, &peers[i], ch)
go peerTest(torrent, peers[i], ch)
}
return ch
}
+10 -10
View File
@@ -11,7 +11,7 @@ import (
type peerPool struct {
torrent *Torrent
states map[peer.Peer]*peerState
states map[*peer.Peer]*peerState
lock *sync.Mutex
}
@@ -26,7 +26,7 @@ func newPeerPool(torrent *Torrent) *peerPool {
return &peerPool{
torrent: torrent,
lock: &sync.Mutex{},
states: map[peer.Peer]*peerState{},
states: map[*peer.Peer]*peerState{},
}
}
@@ -52,7 +52,7 @@ func (pp *peerPool) fetch() {
}()
}
func (pp *peerPool) put(peers []peer.Peer) {
func (pp *peerPool) put(peers []*peer.Peer) {
pp.lock.Lock()
defer pp.lock.Unlock()
for _, peer := range peers {
@@ -72,7 +72,7 @@ func (pp *peerPool) get() *peer.Peer {
for peer, state := range pp.states {
if !state.using {
state.using = true
return &peer
return peer
}
}
return nil
@@ -82,18 +82,18 @@ func (pp *peerPool) get() *peer.Peer {
func (pp *peerPool) release(peer *peer.Peer) {
pp.lock.Lock()
defer pp.lock.Unlock()
pp.states[*peer].using = false
pp.states[*peer].errors = 0
pp.states[peer].using = false
pp.states[peer].errors = 0
}
// 标记peer为不可用,超过3次则剔除该peer
func (pp *peerPool) unavailable(peer *peer.Peer) {
pp.lock.Lock()
defer pp.lock.Unlock()
pp.states[*peer].using = false
pp.states[*peer].errors++
if pp.states[*peer].errors > 3 {
delete(pp.states, *peer)
pp.states[peer].using = false
pp.states[peer].errors++
if pp.states[peer].errors > 3 {
delete(pp.states, peer)
}
}
+8 -26
View File
@@ -7,7 +7,7 @@ import (
"io"
"io/ioutil"
"math"
mrand "math/rand"
"math/rand"
"net"
"net/http"
"net/url"
@@ -47,7 +47,7 @@ func newUdpConnectRequest() *udpConnectRequest {
return &udpConnectRequest{
protocolId: udpConnectRequestMagic,
action: udpActionConnect,
transactionId: mrand.Uint32(),
transactionId: rand.Uint32(),
}
}
@@ -118,7 +118,7 @@ func newUdpAnnounceRequest(connectionId uint64) *udpAnnounceRequest {
return &udpAnnounceRequest{
connectionId: connectionId,
action: udpActionAnnounce,
transactionId: mrand.Uint32(),
transactionId: rand.Uint32(),
}
}
@@ -187,7 +187,7 @@ type Tracker struct {
MetaInfo *metainfo.MetaInfo
}
func (tracker *Tracker) connect(conn *net.UDPConn, timeout int64) (response *udpConnectResponse, err error) {
func (tracker *Tracker) connect(conn net.Conn, timeout int64) (response *udpConnectResponse, err error) {
request := newUdpConnectRequest()
buf := request.encode()
conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeout)))
@@ -204,7 +204,7 @@ func (tracker *Tracker) connect(conn *net.UDPConn, timeout int64) (response *udp
return
}
func (tracker *Tracker) announce(conn *net.UDPConn, timeout int64, connectionId uint64) (response *udpAnnounceResponse, err error) {
func (tracker *Tracker) announce(conn net.Conn, timeout int64, connectionId uint64) (response *udpAnnounceResponse, err error) {
request := newUdpAnnounceRequest(connectionId)
request.infoHash = tracker.MetaInfo.GetInfoHash()
request.peerID = tracker.PeerID
@@ -346,7 +346,7 @@ func (tracker *Tracker) udpTracker(url *url.URL) (peers []*peer.Peer, err error)
var connectResponse *udpConnectResponse
// 0:connect 1:announce
state := udpActionConnect
// 访问超时最多重试8次,当有请求成功则将重试次数重置为0
// 访问超时最多重试8次,当有请求成功则将重试次数重置为0
for n := 0; n < udpConnectRetries; n++ {
// 15 * 2 ^ n seconds
timeout := int64(udpConnectTimeout * math.Pow(2, float64(n)))
@@ -385,26 +385,8 @@ func (tracker *Tracker) udpTracker(url *url.URL) (peers []*peer.Peer, err error)
return announceResponse.peers, nil
}
func dial(trackerUrl *url.URL) (*net.UDPConn, error) {
host, portStr, err := net.SplitHostPort(trackerUrl.Host)
if err != nil {
return nil, err
}
port, err := strconv.ParseInt(portStr, 10, 32)
if err != nil {
return nil, err
}
ip, err := lookupIP(host, trackerUrl.Scheme)
if err != nil {
return nil, err
}
srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
dstAddr := &net.UDPAddr{
IP: ip,
Port: int(port),
}
return net.DialUDP(trackerUrl.Scheme, srcAddr, dstAddr)
func dial(trackerUrl *url.URL) (net.Conn, error) {
return net.Dial(trackerUrl.Scheme, trackerUrl.Host)
}
func lookupIP(host string, scheme string) (ip net.IP, err error) {
@@ -13,11 +13,6 @@ import (
"sync"
)
type Download interface {
Resolve(request *http.Request) (*http.Response, error)
Down(request *http.Request) error
}
// Resolve return the file response to be downloaded
func Resolve(request *Request) (*Response, error) {
httpRequest, err := BuildHTTPRequest(request)
@@ -53,9 +48,9 @@ func Resolve(request *Request) (*Response, error) {
ret.Name = subLastSlash(parse.Path)
}
}
// Unknow file name
// Unknown file name
if ret.Name == "" {
ret.Name = "unknow"
ret.Name = "unknown"
}
// Is support range
ret.Range = response.StatusCode == 206
File renamed without changes.