mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-12 03:14:09 +00:00
feat: http downloader
This commit is contained in:
1 parent
15e5d2af3b
commit
788376eaa9
22 files changed
+813
-64
No files matched your search
@@ -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])
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package dht
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_ping(t *testing.T) {
|
||||
println(ping())
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
Reference in new issue
Block a user