diff --git a/pkg/protocol/bt/client.go b/pkg/protocol/bt/client.go deleted file mode 100644 index 17c907ee..00000000 --- a/pkg/protocol/bt/client.go +++ /dev/null @@ -1,57 +0,0 @@ -package bt - -import ( - "crypto/rand" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/torrent" - "net/url" -) - -type Client struct { - PeerID [20]byte - - // torrents []torrent.Torrent -} - -func NewClient() *Client { - client := &Client{} - - // 生成Peer ID,规则为前三位固定字母(-GP)+SemVer(xyz)+End(-),后面随机生成 - // 参考:https://wiki.theory.org/index.php/BitTorrentSpecification#peer_id - peerID := [20]byte{'-', 'G', 'P', '0', '0', '1', '-'} - _, err := rand.Read(peerID[7:]) - if err != nil { - panic(err) - } - client.PeerID = peerID - - return client -} - -func (client *Client) AddTorrent(rawurl string) error { - parse, err := url.Parse(rawurl) - if err != nil { - return err - } - var metaInfo *metainfo.MetaInfo - switch parse.Scheme { - case "magnet": - // TODO 磁力链接解析 - default: - metaInfo, err = metainfo.ParseFromFile(rawurl) - break - } - if err != nil { - return err - } - - torrent := torrent.Torrent{ - // client: client, - MetaInfo: metaInfo, - } - // client.torrents = append(client.torrents, torrent) - - torrent.Download("") - - return nil -} diff --git a/pkg/protocol/bt/client_test.go b/pkg/protocol/bt/client_test.go deleted file mode 100644 index 1a4c0747..00000000 --- a/pkg/protocol/bt/client_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package bt - -import ( - "testing" -) - -func TestClient_AddTorrent(t *testing.T) { - /*client := NewClient() - metaInfo, err := metainfo.ParseFromFile("testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") - if err != nil { - panic(err) - } - torrent := & - torrent.MetaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce" - torrent.MetaInfo.AnnounceList = [][]string{} - err = torrent.Tracker() - if err != nil { - panic(err) - return - }*/ -} diff --git a/pkg/protocol/bt/dht/ping.go b/pkg/protocol/bt/dht/ping.go deleted file mode 100644 index 3a054be6..00000000 --- a/pkg/protocol/bt/dht/ping.go +++ /dev/null @@ -1,34 +0,0 @@ -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]) -} diff --git a/pkg/protocol/bt/dht/ping_test.go b/pkg/protocol/bt/dht/ping_test.go deleted file mode 100644 index ea0e1e79..00000000 --- a/pkg/protocol/bt/dht/ping_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package dht - -import "testing" - -func Test_ping(t *testing.T) { - println(ping()) -} diff --git a/pkg/protocol/bt/metainfo/metainfo.go b/pkg/protocol/bt/metainfo/metainfo.go deleted file mode 100644 index 8f9c55d9..00000000 --- a/pkg/protocol/bt/metainfo/metainfo.go +++ /dev/null @@ -1,144 +0,0 @@ -package metainfo - -import ( - "crypto/sha1" - "encoding/json" - "os" - - "github.com/marksamman/bencode" -) - -type MetaInfo struct { - // tracker URL - // http://bittorrent.org/beps/bep_0003.html#metainfo-files - Announce string `json:"announce"` - // tracker URL - // http://bittorrent.org/beps/bep_0012.html - AnnounceList [][]string `json:"announce-list"` - Comment string `json:"comment"` - CreatedBy string `json:"created by"` - CreationDate int64 `json:"creation date"` - Encoding string `json:"encoding"` - // http://bittorrent.org/beps/bep_0019.html - UrlList []string `json:"url-list"` - Info *Info `json:"info"` - - infoHash [20]byte - totalSize uint64 - fileDetails []*FileDetail -} - -type Info struct { - Name string `json:"name"` - PieceLength int `json:"piece length"` - Pieces [][20]byte - // 单个文件时,length为该文件的长度 - Length uint64 `json:"length"` - // 多个文件 - Files []*File `json:"files"` -} - -type File struct { - Length uint64 `json:"length"` - Path []string `json:"path"` -} - -type FileDetail struct { - Length uint64 - Path []string - Begin int64 - End int64 -} - -type PieceMapping struct { - file int - begin int64 - end int64 -} - -func ParseFromFile(path string) (*MetaInfo, error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - // http://bittorrent.org/beps/bep_0003.html#bencoding - btDecode, err := bencode.Decode(file) - if err != nil { - return nil, err - } - bytes, err := json.Marshal(btDecode) - if err != nil { - return nil, err - } - var metaInfo MetaInfo - json.Unmarshal(bytes, &metaInfo) - if metaInfo.Info != nil { - // encode info hash - info := btDecode["info"].(map[string]interface{}) - infoBtEncode := bencode.Encode(info) - metaInfo.infoHash = sha1.Sum(infoBtEncode[:]) - // Split pieces hash - pieces := info["pieces"].(string) - if pieces != "" { - metaInfo.Info.Pieces = make([][20]byte, len(pieces)/20) - // pieces parse - for i := range metaInfo.Info.Pieces { - begin := i * 20 - metaInfo.Info.Pieces[i] = [20]byte{} - copy(metaInfo.Info.Pieces[i][:], pieces[begin:begin+20]) - } - } - } - calcFileSize(&metaInfo) - return &metaInfo, nil -} - -// 获取某个分片的大小 -func (metaInfo *MetaInfo) GetPieceLength(index int) int { - // 是否为最后一个分片 - if index == len(metaInfo.Info.Pieces)-1 { - size := metaInfo.totalSize % uint64(metaInfo.Info.PieceLength) - if size > 0 { - return int(size) - } - } - return metaInfo.Info.PieceLength -} - -// 获取所有文件的大小 -func (metaInfo *MetaInfo) GetTotalSize() uint64 { - return metaInfo.totalSize -} - -// 获取info hash -func (metaInfo *MetaInfo) GetInfoHash() [20]byte { - return metaInfo.infoHash -} - -// 获取所有文件的开始偏移字节 -func (metaInfo *MetaInfo) GetFileDetails() []*FileDetail { - return metaInfo.fileDetails -} - -// 计算所有文件的大小 -func calcFileSize(metaInfo *MetaInfo) { - if metaInfo.Info != nil { - fileCount := len(metaInfo.Info.Files) - if fileCount > 0 { - metaInfo.fileDetails = make([]*FileDetail, fileCount) - var length uint64 - for i, f := range metaInfo.Info.Files { - metaInfo.fileDetails[i] = &FileDetail{ - Length: f.Length, - Path: f.Path, - Begin: int64(length), - End: int64(length + f.Length), - } - length += f.Length - } - metaInfo.totalSize = length - } else { - metaInfo.totalSize = metaInfo.Info.Length - } - } -} diff --git a/pkg/protocol/bt/metainfo/metainfo_test.go b/pkg/protocol/bt/metainfo/metainfo_test.go deleted file mode 100644 index e940abfd..00000000 --- a/pkg/protocol/bt/metainfo/metainfo_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package metainfo - -import ( - "encoding/hex" - "fmt" - "testing" -) - -func TestParseFromFile(t *testing.T) { - // metaInfo, err := ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") - metaInfo, err := ParseFromFile("E:\\bt\\win10.torrent") - if err != nil { - panic(err) - } - fmt.Printf("%+v\n", metaInfo.AnnounceList) - 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[:])) -} diff --git a/pkg/protocol/bt/peer/handshake.go b/pkg/protocol/bt/peer/handshake.go deleted file mode 100644 index 5e30913b..00000000 --- a/pkg/protocol/bt/peer/handshake.go +++ /dev/null @@ -1,51 +0,0 @@ -package peer - -import ( - "bytes" - "encoding/binary" -) - -const ( - ProtocolIdentifier = "BitTorrent protocol" - ProtocolIdentifierLength = 0x13 -) - -// Handshake of Peer wire protocol -// Per https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake -type Handshake struct { - Pstrlen byte - Pstr [19]byte - Reserved [8]byte - InfoHash [20]byte - PeerID [20]byte -} - -func NewHandshake(reserved [8]byte, infoHash [20]byte, peerID [20]byte) *Handshake { - var arr [ProtocolIdentifierLength]byte - copy(arr[:], ProtocolIdentifier) - return &Handshake{ - Pstrlen: ProtocolIdentifierLength, - Pstr: arr, - Reserved: reserved, - InfoHash: infoHash, - PeerID: peerID, - } -} - -func (handshake *Handshake) Encode() []byte { - writer := new(bytes.Buffer) - err := binary.Write(writer, binary.BigEndian, handshake) - if err != nil { - panic(err) - } - return writer.Bytes() -} - -func (handshake *Handshake) Decode(buf []byte) error { - reader := bytes.NewReader(buf) - err := binary.Read(reader, binary.BigEndian, handshake) - if err != nil { - return err - } - return nil -} diff --git a/pkg/protocol/bt/peer/message/message.go b/pkg/protocol/bt/peer/message/message.go deleted file mode 100644 index 0fc50fab..00000000 --- a/pkg/protocol/bt/peer/message/message.go +++ /dev/null @@ -1,143 +0,0 @@ -package message - -import ( - "encoding/binary" -) - -// message ID -type ID byte - -const ( - IdChoke ID = iota - IdUnchoke - IdInterested - IdNotInterested - IdHave - IdBitfield - IdRequest - IdPiece - IdCancel -) - -/*type Message struct { - Length uint32 - ID MessageID - Payload []byte -}*/ - -type base struct { - id ID -} - -func (base *base) ID() ID { - return base.id -} - -func (base *base) Encode() []byte { - return encode(base, nil) -} - -func (base *base) Decode(body []byte) { - -} - -type Message interface { - ID() ID - Encode() []byte - Decode(body []byte) -} - -type TestMsg struct { - *base -} - -func (base *TestMsg) Encode() []byte { - return []byte{1, 2, 3} -} - -func (base *TestMsg) Decode(body []byte) { -} - -func encode(base *base, body []byte) []byte { - buf := make([]byte, 5+len(body)) - binary.BigEndian.PutUint32(buf, uint32(len(buf))) - buf[4] = byte(base.id) - copy(buf[4:], body) - return buf -} - -/*func Decode(buf []byte) Message { - head := make([]byte, 5) - body := message.Encode() - binary.BigEndian.PutUint32(head, uint32(len(head)+len(body))) - head[4] = byte(message.ID()) - return append(head, body...) -} - -func (msg *Message) ToBytes() []byte { - buf := make([]byte, 5) - binary.BigEndian.PutUint32(buf, msg.Length) - buf[4] = byte(msg.ID) - if msg.Payload != nil { - return append(buf, msg.Payload.Encode()...) - } else { - return buf - } -} - -func (msg *Message) FormBytes(buf []byte) { - msg.Length = binary.BigEndian.Uint32(buf) - msg.ID = ID(buf[4]) - if msg.Payload != nil { - msg.Payload.Decode(buf[4:]) - } -}*/ - -/*func NewMessage(id MessageID, Payload []byte) *Message { - message := &Message{ID: id, Payload: Payload} - if id == Keepalive { - message.Length = 0 - } else { - message.Length = uint32(len(Payload) + 1) - } - return message -} - -func (msg *Message) Encode() []byte { - if msg.ID == Keepalive { - return make([]byte, 4) - } else { - buf := make([]byte, 5+len(msg.Payload)) - binary.BigEndian.PutUint32(buf, msg.Length) - buf[4] = byte(msg.ID) - copy(buf[5:], msg.Payload[:]) - return buf - } -} - -func (msg *Message) Decode(buf []byte) { - msg.Length = binary.BigEndian.Uint32(buf) - if msg.Length == 0 { - msg.ID = -1 - } else { - msg.ID = MessageID(buf[4]) - if msg.Length > 4 { - msg.Payload = buf[5:] - } - } -}*/ - -// Message protocol -// Length prefix| message ID | Payload -// 4-byte | 1-byte | remaining bytes -// 100 | 5 | 99-bytes -// https://wiki.theory.org/index.php/BitTorrentSpecification#Messages -func SplitMessage(data []byte, atEOF bool) (advance int, token []byte, err error) { - if !atEOF && len(data) > 4 { - length := int(binary.BigEndian.Uint32(data)) - if len(data)-4 >= length { - return length + 4, data[:length+4], nil - } - } - return -} diff --git a/pkg/protocol/bt/peer/message/msg_bitfield.go b/pkg/protocol/bt/peer/message/msg_bitfield.go deleted file mode 100644 index 73758181..00000000 --- a/pkg/protocol/bt/peer/message/msg_bitfield.go +++ /dev/null @@ -1,68 +0,0 @@ -package message - -import ( - "github.com/RoaringBitmap/roaring" - "math" -) - -// bitfield: -type Bitfield struct { - *base - pieceCount int - bitmap *roaring.Bitmap -} - -func NewBitfield() *Bitfield { - return &Bitfield{ - base: &base{ - id: IdBitfield, - }, - } -} - -func BuildBitfield(pieceCount int, bitmap *roaring.Bitmap) *Bitfield { - bitfield := NewBitfield() - bitfield.pieceCount = pieceCount - bitfield.bitmap = bitmap - return bitfield -} - -func (b *Bitfield) Encode() []byte { - buf := make([]byte, int(math.Ceil(float64(b.pieceCount)/8))) - for i := 0; i < len(buf); i++ { - temp := byte(0) - for j := 0; j < 8; j++ { - // 如果piece存在,位数标识为1 - if b.bitmap.ContainsInt(i*8 + j) { - temp = temp | (1 << (7 - j)) - } - } - buf[i] = temp - } - return encode(b.base, buf) -} - -func (b *Bitfield) Decode(body []byte) { - b.bitmap = roaring.New() - // 初始化bitmap - for i := 0; i < len(body); i++ { - for j := 0; j < 8; j++ { - if body[i]&(1<<(7-j)) > 0 { - b.bitmap.AddInt(i*8 + j) - } - } - } -} - -// 某个分片是否下载完成 -func (b *Bitfield) IsComplete(i int) bool { - return b.bitmap.ContainsInt(i) -} - -// 给定一组分片下载状态,计算出当前peer能提供下载的分片下标,如果已经拥有则不用提供 -// has = 0b01111111,had = 0b10010000 -// has & ~had = 0b01111111 & 0b01101111 = 0b01101111 -func (b *Bitfield) Provide(had *roaring.Bitmap) []uint32 { - xor := roaring.AndNot(b.bitmap, had) - return xor.ToArray() -} diff --git a/pkg/protocol/bt/peer/message/msg_bitfield_test.go b/pkg/protocol/bt/peer/message/msg_bitfield_test.go deleted file mode 100644 index 7e0e8c48..00000000 --- a/pkg/protocol/bt/peer/message/msg_bitfield_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package message - -import ( - "github.com/RoaringBitmap/roaring" - "reflect" - "testing" -) - -func TestBitfield_IsComplete(t *testing.T) { - type fields struct { - payload *roaring.Bitmap - } - type args struct { - i int - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - "case-1", - fields{ - payload: roaring.BitmapOf(1, 2, 3, 4), - }, - args{ - 1, - }, - true, - }, - { - "case-2", - fields{ - payload: roaring.BitmapOf(1, 2, 3, 4), - }, - args{ - 5, - }, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - b := &Bitfield{ - pieceCount: 100, - bitmap: tt.fields.payload, - } - if got := b.IsComplete(tt.args.i); got != tt.want { - t.Errorf("Bitfield.IsComplete() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestBitfield_Provide(t *testing.T) { - type fields struct { - payload *roaring.Bitmap - } - type args struct { - had *roaring.Bitmap - } - tests := []struct { - name string - fields fields - args args - want []uint32 - }{ - { - "case-1", - fields{ - payload: roaring.BitmapOf(0, 1, 2, 3, 4), - }, - args{ - roaring.BitmapOf(0), - }, - []uint32{1, 2, 3, 4}, - }, - { - "case-2", - fields{ - payload: roaring.BitmapOf(0, 1, 2, 3, 4, 5, 6, 7, 8), - }, - args{ - roaring.BitmapOf(0, 1, 3, 5, 6, 8), - }, - []uint32{2, 4, 7}, - }, - { - "case-3", - fields{ - payload: roaring.BitmapOf(), - }, - args{ - roaring.BitmapOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), - }, - []uint32{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - b := &Bitfield{ - pieceCount: 100, - bitmap: tt.fields.payload, - } - if got := b.Provide(tt.args.had); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Bitfield.Provide() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/protocol/bt/peer/message/msg_choke.go b/pkg/protocol/bt/peer/message/msg_choke.go deleted file mode 100644 index e7446914..00000000 --- a/pkg/protocol/bt/peer/message/msg_choke.go +++ /dev/null @@ -1,14 +0,0 @@ -package message - -// choke: -type Choke struct { - *base -} - -func NewChoke() *Choke { - return &Choke{ - base: &base{ - id: IdChoke, - }, - } -} diff --git a/pkg/protocol/bt/peer/message/msg_interested.go b/pkg/protocol/bt/peer/message/msg_interested.go deleted file mode 100644 index ab17bec7..00000000 --- a/pkg/protocol/bt/peer/message/msg_interested.go +++ /dev/null @@ -1,14 +0,0 @@ -package message - -// choke: -type Interested struct { - *base -} - -func NewInterested() *Interested { - return &Interested{ - base: &base{ - id: IdInterested, - }, - } -} diff --git a/pkg/protocol/bt/peer/message/msg_keepalive.go b/pkg/protocol/bt/peer/message/msg_keepalive.go deleted file mode 100644 index 14633c53..00000000 --- a/pkg/protocol/bt/peer/message/msg_keepalive.go +++ /dev/null @@ -1,16 +0,0 @@ -package message - -// keep-alive: -type Keepalive struct { - *base -} - -var data = make([]byte, 4) - -func (k *Keepalive) Encode() []byte { - return data -} - -func (k *Keepalive) Decode(body []byte) { - -} diff --git a/pkg/protocol/bt/peer/message/msg_not_interested.go b/pkg/protocol/bt/peer/message/msg_not_interested.go deleted file mode 100644 index cbb9eab4..00000000 --- a/pkg/protocol/bt/peer/message/msg_not_interested.go +++ /dev/null @@ -1,14 +0,0 @@ -package message - -// choke: -type NotInterested struct { - *base -} - -func NewNotInterested() *NotInterested { - return &NotInterested{ - base: &base{ - id: IdNotInterested, - }, - } -} diff --git a/pkg/protocol/bt/peer/message/msg_piece.go b/pkg/protocol/bt/peer/message/msg_piece.go deleted file mode 100644 index 182b088d..00000000 --- a/pkg/protocol/bt/peer/message/msg_piece.go +++ /dev/null @@ -1,42 +0,0 @@ -package message - -import "encoding/binary" - -// piece: -type Piece struct { - *base - Index uint32 - Begin uint32 - Block []byte -} - -func NewPiece() *Piece { - return &Piece{ - base: &base{ - id: IdPiece, - }, - } -} - -func BuildPiece(index uint32, begin uint32, block []byte) *Piece { - piece := NewPiece() - piece.Index = index - piece.Begin = begin - piece.Block = block - return piece - -} - -func (p *Piece) Encode() []byte { - buf := make([]byte, 8+len(p.Block)) - binary.BigEndian.PutUint32(buf[0:4], p.Index) - binary.BigEndian.PutUint32(buf[4:8], p.Begin) - copy(buf[8:], p.Block) - return encode(p.base, buf) -} - -func (p *Piece) Decode(buf []byte) { - p.Index = binary.BigEndian.Uint32(buf[0:4]) - p.Begin = binary.BigEndian.Uint32(buf[4:8]) - p.Block = buf[8:] -} diff --git a/pkg/protocol/bt/peer/message/msg_request.go b/pkg/protocol/bt/peer/message/msg_request.go deleted file mode 100644 index 88165921..00000000 --- a/pkg/protocol/bt/peer/message/msg_request.go +++ /dev/null @@ -1,43 +0,0 @@ -package message - -import ( - "encoding/binary" -) - -// request: -type Request struct { - *base - Index uint32 - Begin uint32 - Length uint32 -} - -func NewRequest() *Request { - return &Request{ - base: &base{ - id: IdRequest, - }, - } -} - -func BuildRequest(index uint32, begin uint32, length uint32) *Request { - request := NewRequest() - request.Index = index - request.Begin = begin - request.Length = length - return request -} - -func (r *Request) Encode() []byte { - buf := make([]byte, 12) - binary.BigEndian.PutUint32(buf[0:4], r.Index) - binary.BigEndian.PutUint32(buf[4:8], r.Begin) - binary.BigEndian.PutUint32(buf[8:12], r.Length) - return encode(r.base, buf) -} - -func (r *Request) Decode(buf []byte) { - r.Length = binary.BigEndian.Uint32(buf[0:4]) - r.Begin = binary.BigEndian.Uint32(buf[4:8]) - r.Index = binary.BigEndian.Uint32(buf[8:12]) -} diff --git a/pkg/protocol/bt/peer/message/msg_unchoke.go b/pkg/protocol/bt/peer/message/msg_unchoke.go deleted file mode 100644 index 2fd6e994..00000000 --- a/pkg/protocol/bt/peer/message/msg_unchoke.go +++ /dev/null @@ -1,14 +0,0 @@ -package message - -// unchoke: -type Unchoke struct { - *base -} - -func NewUnchoke() *Unchoke { - return &Unchoke{ - base: &base{ - id: IdUnchoke, - }, - } -} diff --git a/pkg/protocol/bt/peer/peer.go b/pkg/protocol/bt/peer/peer.go deleted file mode 100644 index f36465f0..00000000 --- a/pkg/protocol/bt/peer/peer.go +++ /dev/null @@ -1,30 +0,0 @@ -package peer - -import ( - "encoding/binary" - "math/rand" - "net" - "strconv" -) - -type Peer struct { - IP uint32 - Port uint16 -} - -func (peer *Peer) Address() string { - bts := make([]byte, 4) - binary.BigEndian.PutUint32(bts, peer.IP) - return net.IP(bts).String() + ":" + strconv.Itoa(int(peer.Port)) -} - -// 生成Peer ID,规则为前三位固定字母(-GP)+SemVer(xyz)+End(-),后面随机生成 -// 参考:https://wiki.theory.org/index.php/BitTorrentSpecification#peer_id -func GenPeerID() [20]byte { - peerID := [20]byte{'-', 'G', 'P', '0', '0', '1', '-'} - _, err := rand.Read(peerID[7:]) - if err != nil { - panic(err) - } - return peerID -} diff --git a/pkg/protocol/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent b/pkg/protocol/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent deleted file mode 100644 index bad833c4..00000000 Binary files a/pkg/protocol/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent and /dev/null differ diff --git a/pkg/protocol/bt/testdata/office.torrent b/pkg/protocol/bt/testdata/office.torrent deleted file mode 100644 index 15768d09..00000000 Binary files a/pkg/protocol/bt/testdata/office.torrent and /dev/null differ diff --git a/pkg/protocol/bt/torrent/main/main.go b/pkg/protocol/bt/torrent/main/main.go deleted file mode 100644 index 7bb0e09b..00000000 --- a/pkg/protocol/bt/torrent/main/main.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/torrent" - log "github.com/sirupsen/logrus" -) - -func main() { - log.SetLevel(log.DebugLevel) - torrent := buildTorrent() - torrent.Download("e:/testbt/download") -} - -func buildTorrent() *torrent.Torrent { - // metaInfo, err := metainfo.ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") - metaInfo, err := metainfo.ParseFromFile("../../testdata/office.torrent") - if err != nil { - panic(err) - } - - metaInfo.AnnounceList[0] = append(metaInfo.AnnounceList[0], []string{ - "udp://tracker.coppersurfer.tk:6969/announce", - "udp://tracker.leechers-paradise.org:6969/announce", - "udp://tracker.opentrackr.org:1337/announce", - "udp://p4p.arenabg.com:1337/announce", - "udp://9.rarbg.to:2710/announce", - "udp://9.rarbg.me:2710/announce", - "udp://tracker.internetwarriors.net:1337/announce", - "udp://exodus.desync.com:6969/announce", - "udp://tracker.tiny-vps.com:6969/announce", - "udp://tracker.sbsub.com:2710/announce", - "udp://retracker.lanta-net.ru:2710/announce", - "udp://open.stealth.si:80/announce", - "udp://open.demonii.si:1337/announce", - "udp://tracker.torrent.eu.org:451/announce", - "udp://tracker.moeking.me:6969/announce", - "udp://tracker.cyberia.is:6969/announce", - "udp://denis.stalker.upeer.me:6969/announce", - "udp://ipv4.tracker.harry.lu:80/announce", - "udp://explodie.org:6969/announce", - "udp://zephir.monocul.us:6969/announce", - "udp://xxxtor.com:2710/announce", - "udp://valakas.rollo.dnsabr.com:2710/announce", - "udp://tracker.zerobytes.xyz:1337/announce", - "udp://tracker.yoshi210.com:6969/announce", - "udp://tracker.uw0.xyz:6969/announce", - "udp://tracker.swateam.org.uk:2710/announce", - "udp://tracker.nyaa.uk:6969/announce", - "udp://tracker.filemail.com:6969/announce", - "udp://tracker.ds.is:6969/announce", - "udp://retracker.sevstar.net:2710/announce", - "udp://retracker.netbynet.ru:2710/announce", - "udp://retracker.akado-ural.ru:80/announce", - "udp://opentracker.i2p.rocks:6969/announce", - "udp://opentor.org:2710/announce", - "udp://open.nyap2p.com:6969/announce", - "udp://chihaya.toss.li:9696/announce", - "udp://bt2.archive.org:6969/announce", - "udp://bt1.archive.org:6969/announce", - "udp://www.loushao.net:8080/announce", - "udp://tracker4.itzmx.com:2710/announce", - "udp://tracker3.itzmx.com:6961/announce", - "udp://tracker2.itzmx.com:6961/announce", - "udp://tracker.lelux.fi:6969/announce", - "udp://tracker.kamigami.org:2710/announce", - "udp://tracker.dler.org:6969/announce", - "udp://tr.bangumi.moe:6969/announce", - "udp://qg.lorzl.gq:2710/announce", - "udp://bt2.54new.com:8080/announce", - }...) - return torrent.NewTorrent(peer.GenPeerID(), metaInfo) -} diff --git a/pkg/protocol/bt/torrent/peer_conn.go b/pkg/protocol/bt/torrent/peer_conn.go deleted file mode 100644 index e90a574a..00000000 --- a/pkg/protocol/bt/torrent/peer_conn.go +++ /dev/null @@ -1,464 +0,0 @@ -package torrent - -import ( - "bufio" - "crypto/sha1" - "encoding/binary" - "errors" - "fmt" - "github.com/RoaringBitmap/roaring" - "io" - "net" - "os" - "path" - "path/filepath" - "time" - - "github.com/cenkalti/mse" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer/message" - log "github.com/sirupsen/logrus" -) - -// 现在主流客户端的block大小都是16KB -const blockSize = 2 << 13 -const keepaliveTimeout = 60 * 2 - -var errPieceCheckFailed = errors.New("piece check failed") -var keepaliveData = make([]byte, 4) - -type peerConn struct { - torrent *Torrent - peer *peer.Peer - conn net.Conn - // this client is choking the peer - amChoking bool - // this client is interested in the peer - amInterested bool - // peer is choking this client - peerChoking bool - // peer is interested in this client - peerInterested bool - - // 上一次收到包的时间 - lastReciveTime int64 - - bitfield *message.Bitfield - readyEnd bool - - downloadedCh chan error - disconnectCh chan error - // block下载队列,官方推荐为5 - blockQueueCh chan interface{} -} - -func newPeerConn(torrent *Torrent, peer *peer.Peer) *peerConn { - return &peerConn{ - torrent: torrent, - peer: peer, - } -} - -// 使用MSE加密来避免运营商对bt流量的封锁,基本上现在市面上BT客户端都默认开启了,不用MSE的话很多Peer拒绝连接 -// see http://wiki.vuze.com/w/Message_Stream_Encryption -func (pc *peerConn) dialMse() error { - conn, err := net.DialTimeout("tcp", pc.peer.Address(), time.Second*time.Duration(30)) - if err != nil { - return err - } - mseConn := mse.WrapConn(conn) - infoHash := pc.torrent.MetaInfo.GetInfoHash() - _, err = mseConn.HandshakeOutgoing(infoHash[:], mse.PlainText, nil) - if err != nil { - mseConn.Close() - return err - } - pc.conn = mseConn - return nil -} - -// Handshake of Peer wire protocol -// see https://wiki.theory.org/index.php/BitTorrentSpecification#Handshake -func (pc *peerConn) handshake() (*peer.Handshake, error) { - handshakeRes, err := func() (*peer.Handshake, error) { - handshakeReq := peer.NewHandshake([8]byte{}, pc.torrent.MetaInfo.GetInfoHash(), pc.torrent.PeerID) - _, err := pc.conn.Write(handshakeReq.Encode()) - if err != nil { - return nil, err - } - var read [68]byte - _, err = io.ReadFull(pc.conn, read[:]) - if err != nil { - return nil, err - } - handshakeRes := &peer.Handshake{} - err = handshakeRes.Decode(read[:]) - if err != nil { - return nil, err - } - // InfoHash不匹配 - if handshakeRes.InfoHash != handshakeReq.InfoHash { - return nil, fmt.Errorf("info_hash not currently serving") - } - return handshakeRes, nil - }() - if err != nil { - pc.conn.Close() - return nil, err - } - // init state - pc.amChoking = true - pc.amInterested = false - pc.peerChoking = true - pc.peerInterested = false - return handshakeRes, nil -} - -func (pc *peerConn) handleKeepalive() { - pc.lastReciveTime = time.Now().Unix() - for { - time.Sleep(time.Minute * 2) - // 如果超过两分钟没有响应,则断开连接 - if time.Now().Unix()-pc.lastReciveTime > keepaliveTimeout { - pc.conn.Close() - break - } - // 两分钟发送一次心跳包 - _, err := pc.conn.Write(keepaliveData) - if err != nil { - pc.conn.Close() - break - } - } -} - -// 准备下载 -func (pc *peerConn) ready() error { - if err := pc.dialMse(); err != nil { - return fmt.Errorf("tcp dial error %w", err) - } - if _, err := pc.handshake(); err != nil { - return fmt.Errorf("handshake error %w", err) - } - pc.readyEnd = false - readyCh := make(chan bool) - pc.disconnectCh = make(chan error) - go pc.handleKeepalive() - go func() { - scanner := bufio.NewScanner(pc.conn) - scanner.Split(message.SplitMessage) - for scanner.Scan() { - pc.lastReciveTime = time.Now().Unix() - buf := scanner.Bytes() - length := binary.BigEndian.Uint32(buf[:4]) - if length == 0 { - // keepalive - } else { - switch message.ID(buf[4]) { - case message.IdChoke: - break - case message.IdUnchoke: - pc.handleUnchoke(readyCh) - break - case message.IdInterested: - break - case message.IdNotInterested: - break - case message.IdHave: - break - case message.IdBitfield: - pc.handleBitfield(buf) - break - case message.IdRequest: - break - case message.IdPiece: - pc.handlePiece(buf) - break - case message.IdCancel: - break - } - } - } - // 还未下载完成时连接断开 - err := scanner.Err() - if err == nil { - err = io.EOF - } - pc.disconnectCh <- err - close(pc.disconnectCh) - }() - err := func() error { - select { - case status := <-readyCh: - if status { - return nil - } - return errors.New("ready fail") - case <-time.After(time.Second * 30): - // 30秒之后超时 - return errors.New("ready time out") - } - }() - if err != nil { - pc.conn.Close() - return err - } - return nil -} - -// 下载指定piece -func (pc *peerConn) downloadPiece(index int) (err error) { - pieceLength := pc.torrent.MetaInfo.GetPieceLength(index) - pc.downloadedCh = make(chan error) - pc.blockQueueCh = make(chan interface{}, 5) - defer close(pc.blockQueueCh) - // 按块下载分片 - blockCount := pc.torrent.pieceStates.states[index].blockCount - for i := 0; i < blockCount; i++ { - offset := blockSize * i - // 如果已经下载过就跳过 - if pc.torrent.pieceStates.isBlockDone(index, offset) { - continue - } - - var blockLength uint32 - if i == blockCount-1 { - // 最后一个block大小需要计算出来 - blockLength = uint32(pieceLength - offset) - } else { - blockLength = blockSize - } - // block下载排队 - select { - case pc.blockQueueCh <- nil: - break - case err = <-pc.downloadedCh: - break - case err = <-pc.disconnectCh: - break - } - // 如果连接出现问题或下载失败直接返回异常 - if err != nil { - pc.conn.Close() - return - } - // 发起request,对方会响应piece - _, err = pc.conn.Write(message.BuildRequest(uint32(index), uint32(offset), blockLength).Encode()) - if err != nil { - break - } - } - select { - case err = <-pc.downloadedCh: - break - case err = <-pc.disconnectCh: - break - } - if err != nil { - pc.conn.Close() - } - return -} - -func (pc *peerConn) handleUnchoke(readyCh chan<- bool) { - pc.peerChoking = false - // 已经处理过Unchoke信号 - if pc.readyEnd { - return - } - pc.readyEnd = true - // 如果客户端对peer感兴趣并且peer没有choked客户端,就可以开始下载了 - if pc.amInterested { - readyCh <- true - } else { - readyCh <- false - } - close(readyCh) -} - -func (pc *peerConn) handleBitfield(buf []byte) { - pc.bitfield = message.NewBitfield() - pc.bitfield.Decode(buf) - have := pc.getHavePieces(pc.bitfield) - if len(have) > 0 { - // 表示对该peer感兴趣,并且不choked该peer - pc.conn.Write(message.NewInterested().Encode()) - pc.amInterested = true - - pc.conn.Write(message.NewUnchoke().Encode()) - pc.amChoking = false - } else { - pc.conn.Close() - } -} - -// 处理下载响应,每次接收到响应直接将block写入到对应文件中 -func (pc *peerConn) handlePiece(buf []byte) { - piece := message.NewPiece() - piece.Decode(buf) - info := pc.torrent.MetaInfo.Info - fds := pc.torrent.MetaInfo.GetFileDetails() - blockLength := int64(len(piece.Block)) - pieceBegin := int64(piece.Index) * int64(info.PieceLength) - blockBegin := pieceBegin + int64(piece.Begin) - // 计算block对应要写到的文件偏移 - var fileBlocks []*fileBlock - if len(info.Files) == 0 { - // 单文件 - fileBlocks = append(fileBlocks, &fileBlock{ - filepath: info.Name, - fileSeek: blockBegin, - blockBegin: 0, - blockEnd: blockLength, - }) - } else { - // 获取要写入的第一个文件和偏移 - writeIndex := getWriteFile(blockBegin, fds) - // block对应种子的偏移 - blockFileBegin := blockBegin - // block对应文件的偏移 - var blockSeek int64 = 0 - for _, f := range fds[writeIndex:] { - // 计算文件可剩余写入字节数 - fileWritable := f.End - blockFileBegin - // 计算block剩余写入字节数 - blockWritable := blockLength - blockSeek - fb := &fileBlock{ - filepath: filepath.Join(f.Path...), - fileSeek: blockFileBegin - f.Begin, - blockBegin: blockSeek, - } - if fileWritable >= blockWritable { - // 若够block写入直接跳出循环 - fb.blockEnd = blockSeek + blockWritable - fileBlocks = append(fileBlocks, fb) - break - } else { - // 否则计算剩余可写字节数,写入到下一个文件中 - fb.blockEnd = blockSeek + fileWritable - fileBlocks = append(fileBlocks, fb) - blockFileBegin = f.End - blockSeek += fileWritable - } - } - } - // 开始写入文件 - for _, f := range fileBlocks { - err := func() error { - name := filepath.Join(pc.torrent.Path, f.filepath) - file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0644) - if err != nil { - return err - } - defer file.Close() - _, err = file.WriteAt(piece.Block[f.blockBegin:f.blockEnd], f.fileSeek) - if err != nil { - return err - } - return nil - }() - if err != nil { - pc.downloadedCh <- errPieceCheckFailed - pc.conn.Close() - return - } - } - pc.torrent.pieceStates.setBlockDone(int(piece.Index), int(piece.Begin)) - // 出队 - <-pc.blockQueueCh - - log.Debugf("piece:%d block:%d", piece.Index, piece.Begin) - - // piece全部下载完 - if pc.torrent.pieceStates.isPieceDone(int(piece.Index)) { - // 计算piece对应的文件偏移 - sha1 := sha1.New() - pieceLength := pc.torrent.MetaInfo.GetPieceLength(int(piece.Index)) - writeIndex := getWriteFile(pieceBegin, fds) - fileBegin := pieceBegin - fds[writeIndex].Begin - for _, fd := range fds[writeIndex:] { - func() { - file, err := os.Open(path.Join(pc.torrent.Path, fd.Path[0])) - if err != nil { - panic(err) - } - defer file.Close() - _, err = file.Seek(fileBegin, 0) - if err != nil { - panic(err) - } - written, err := io.CopyN(sha1, file, int64(pieceLength)) - if err != nil { - if err != io.EOF { - panic(err) - } - } - pieceLength -= int(written) - }() - if pieceLength > 0 { - // 继续读下个文件 - fileBegin = 0 - } else { - break - } - } - // 断开连接 TODO 用连接池进行复用 - pc.conn.Close() - - // 校验piece SHA-1 hash - downHash := [20]byte{} - copy(downHash[:], sha1.Sum(nil)) - if downHash == pc.torrent.MetaInfo.Info.Pieces[piece.Index] { - // piece下载完成 - pc.downloadedCh <- nil - } else { - pc.downloadedCh <- errPieceCheckFailed - } - close(pc.downloadedCh) - } -} - -// 获取piece对应要写入的文件 -// TODO 使用sort.Search二分查找法检索 -func getWriteFile(pieceBegin int64, fds []*metainfo.FileDetail) int { - for i, f := range fds { - if f.Begin <= pieceBegin && f.End > pieceBegin { - return i - } - } - return -1 -} - -type fileBlock struct { - filepath string - fileSeek int64 - blockBegin int64 - blockEnd int64 -} - -// 获取peer能提供需要下载的文件分片 -func (pc *peerConn) getHavePieces(bitfield *message.Bitfield) []uint32 { - had := roaring.New() - for i := 0; i < pc.torrent.pieceStates.size(); i++ { - if pc.torrent.pieceStates.getState(i) == stateFinish { - had.AddInt(i) - } - } - return bitfield.Provide(had) -} - -// 获取要写入到的文件 -/*func (ps *peerConn) getWriteFile(request *message.Request) string { - info := ps.torrent.MetaInfo.Info - // 单文件 - if len(info.Files) == 0 { - return info.Name - } else { - request.Index * info.PieceLength + be - for i := 0; i < len(info.Files); i++ { - - } - } -} -*/ diff --git a/pkg/protocol/bt/torrent/peer_conn_test.go b/pkg/protocol/bt/torrent/peer_conn_test.go deleted file mode 100644 index 07e38bd2..00000000 --- a/pkg/protocol/bt/torrent/peer_conn_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package torrent - -import ( - "fmt" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/tracker" - "os" - "sync" - "testing" - "time" -) - -func Test_peerState_ready(t *testing.T) { - - ps := getUsablePeer() - fmt.Println("get a peer") - - // 下载前准备 - err := ps.ready() - fmt.Println("ready end") - if err != nil { - fmt.Println(ps.peer.Address()) - t.Fatal(err) - } - -} - -func Test_peerState_downloadPiece(t *testing.T) { - - ps := getUsablePeer() - fmt.Println("get a peer") - - // 下载前准备 - err := ps.ready() - if err != nil { - t.Fatal(err) - } - have := ps.getHavePieces(ps.bitfield) - fmt.Printf("download is ready,have %d\n", len(have)) - if len(have) > 0 { - // 获取分片的长度 - for _, index := range have { - fmt.Printf("download index %d\n", index) - ps.downloadPiece(int(index)) - } - return - } -} - -// 获取一个能连接上的peer -func getUsablePeer() *peerConn { - ch := getUsablePeerMore() - ps := <-ch - return ps -} - -// 获取多个能连接上的peer -func getUsablePeerMore() chan *peerConn { - torrent := buildTorrent() - tracker := &tracker.Tracker{ - PeerID: torrent.PeerID, - MetaInfo: torrent.MetaInfo, - } - peers := <-tracker.Tracker() - ch := make(chan *peerConn) - for i := range peers { - go peerTest(torrent, peers[i], ch) - } - return ch -} - -func peerTest(torrent *Torrent, peer *peer.Peer, ch chan *peerConn) { - ps := &peerConn{ - torrent: torrent, - peer: peer, - } - // 连接至peer - err := ps.ready() - if err != nil { - return - } - ps.conn.Close() - ch <- ps -} - -func TestSome(t *testing.T) { - ch := make(chan interface{}) - - go func() { - time.Sleep(time.Second) - select { - case ch <- nil: - fmt.Println("write") - case <-time.After(time.Second * 3): - fmt.Println("timeout") - } - }() - - time.Sleep(time.Second * 5) - r := <-ch - fmt.Println(r) - fmt.Println("111") - -} - -func TestSome2(t *testing.T) { - wg := sync.WaitGroup{} - wg.Add(10) - file, err := os.OpenFile("e:/testbt/test.data", os.O_RDWR|os.O_CREATE, 0644) - for i := 0; i < 10; i++ { - go func(i int) { - for j := 0; j < 100; j++ { - if err != nil { - panic(err) - } - _, err = file.WriteAt([]byte{byte(i)}, int64(i*8)) - } - wg.Done() - }(i) - } - wg.Wait() -} diff --git a/pkg/protocol/bt/torrent/peer_pool.go b/pkg/protocol/bt/torrent/peer_pool.go deleted file mode 100644 index c126d8d4..00000000 --- a/pkg/protocol/bt/torrent/peer_pool.go +++ /dev/null @@ -1,99 +0,0 @@ -package torrent - -import ( - "sync" - "time" - - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/tracker" -) - -type peerPool struct { - torrent *Torrent - - states map[*peer.Peer]*peerState - lock *sync.Mutex -} - -type peerState struct { - // 是否在被使用 - using bool - // 下载失败次数 - errors int -} - -func newPeerPool(torrent *Torrent) *peerPool { - return &peerPool{ - torrent: torrent, - lock: &sync.Mutex{}, - states: map[*peer.Peer]*peerState{}, - } -} - -func (pp *peerPool) fetch() { - tracker := &tracker.Tracker{ - PeerID: pp.torrent.PeerID, - MetaInfo: pp.torrent.MetaInfo, - } - - go func() { - for { - // 当peer数量少于200个时重新发起一次tracker - if len(pp.states) < 200 { - go func() { - for peers := range tracker.Tracker() { - pp.put(peers) - } - }() - } - // 每2分钟检测一次 - time.Sleep(time.Minute * 2) - } - }() -} - -func (pp *peerPool) put(peers []*peer.Peer) { - pp.lock.Lock() - defer pp.lock.Unlock() - for _, peer := range peers { - if _, ok := pp.states[peer]; !ok { - pp.states[peer] = &peerState{ - using: false, - errors: 0, - } - } - - } -} - -func (pp *peerPool) get() *peer.Peer { - pp.lock.Lock() - defer pp.lock.Unlock() - for peer, state := range pp.states { - if !state.using { - state.using = true - return peer - } - } - return nil -} - -// 将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 -} - -// 标记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) - } - -} diff --git a/pkg/protocol/bt/torrent/piece_state.go b/pkg/protocol/bt/torrent/piece_state.go deleted file mode 100644 index 82133735..00000000 --- a/pkg/protocol/bt/torrent/piece_state.go +++ /dev/null @@ -1,109 +0,0 @@ -package torrent - -import ( - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - "math" - "sync" - - "github.com/RoaringBitmap/roaring" -) - -type state int - -const ( - stateReady = iota - stateDownloading - stateFinish -) - -// 所有piece的下载状态 -type pieceStates struct { - lock *sync.RWMutex - // 所有piece的block下载进度 - states map[int]*pieceState -} - -type pieceState struct { - state state - // piece中block的个数 - blockCount int - // piece中block的下载记录 - blockRecord *roaring.Bitmap -} - -func newPiecesState(meta *metainfo.MetaInfo) *pieceStates { - pieceCount := len(meta.Info.Pieces) - ps := &pieceStates{ - lock: &sync.RWMutex{}, - states: make(map[int]*pieceState, pieceCount), - } - for i := 0; i < pieceCount; i++ { - ps.states[i] = &pieceState{ - state: stateReady, - blockCount: int(math.Ceil(float64(meta.GetPieceLength(i)) / blockSize)), - blockRecord: roaring.New(), - } - } - return ps -} - -func (ps *pieceStates) getState(index int) state { - ps.lock.RLock() - defer ps.lock.RUnlock() - return ps.states[index].state -} - -func (ps *pieceStates) setState(index int, state state) { - ps.lock.Lock() - defer ps.lock.Unlock() - ps.states[index].state = state -} - -func (ps *pieceStates) size() int { - return len(ps.states) -} - -func (ps *pieceStates) getLeft() int { - ps.lock.RLock() - defer ps.lock.RUnlock() - count := 0 - for _, v := range ps.states { - if v.state == stateReady || v.state == stateDownloading { - count++ - } - } - return count -} - -func (ps *pieceStates) isDone() bool { - ps.lock.RLock() - defer ps.lock.RUnlock() - for _, v := range ps.states { - if v.state == stateReady || v.state == stateDownloading { - return false - } - } - return true -} - -func (ps *pieceStates) isPieceDone(index int) bool { - ps.lock.RLock() - defer ps.lock.RUnlock() - state := ps.states[index] - return uint64(state.blockCount) == state.blockRecord.GetCardinality() -} - -// 指定piece下的某个block是否下载完成 -func (ps *pieceStates) isBlockDone(pieceIndex int, blockIndex int) bool { - return ps.states[pieceIndex].blockRecord.ContainsInt(blockIndex) -} - -// 指定piece下的某个block下载完成 -func (ps *pieceStates) setBlockDone(pieceIndex int, blockIndex int) { - ps.states[pieceIndex].blockRecord.AddInt(blockIndex) -} - -// 清空指定piece下所有的block下载记录 -func (ps *pieceStates) clearBlocks(index int) { - ps.states[index].blockRecord.Clear() -} diff --git a/pkg/protocol/bt/torrent/torrent.go b/pkg/protocol/bt/torrent/torrent.go deleted file mode 100644 index f11c3a14..00000000 --- a/pkg/protocol/bt/torrent/torrent.go +++ /dev/null @@ -1,83 +0,0 @@ -package torrent - -import ( - "errors" - "time" - - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - log "github.com/sirupsen/logrus" -) - -type Torrent struct { - PeerID [20]byte - MetaInfo *metainfo.MetaInfo - - pieceStates *pieceStates - peerPool *peerPool - Path string -} - -func NewTorrent(peerID [20]byte, metaInfo *metainfo.MetaInfo) *Torrent { - torrent := &Torrent{ - PeerID: peerID, - MetaInfo: metaInfo, - pieceStates: newPiecesState(metaInfo), - } - return torrent -} - -func (t *Torrent) Download(path string) { - t.Path = path - t.peerPool = newPeerPool(t) - t.peerPool.fetch() - - pieceQueueCh := make(chan interface{}, 128) - defer close(pieceQueueCh) - downloadedCh := make(chan interface{}) - defer close(downloadedCh) - - for i := 0; i < len(t.MetaInfo.Info.Pieces); i++ { - // 加入下载队列 - pieceQueueCh <- nil - go func(index int) { - t.pieceStates.setState(index, stateDownloading) - // 一直下载到成功 - for { - peer := t.peerPool.get() - if peer == nil { - time.Sleep(time.Second * 10) - continue - } - pc := newPeerConn(t, peer) - err := pc.ready() - if err != nil { - t.peerPool.unavailable(peer) - } else { - err = pc.downloadPiece(index) - if err != nil { - log.Debugf("piece %d download fail:%s", index, err.Error()) - // 下载失败 - t.peerPool.unavailable(peer) - if errors.Is(err, errPieceCheckFailed) { - // piece校验失败,需要重新下载过一遍,清空block记录 - t.pieceStates.clearBlocks(index) - } - continue - } else { - // 下载完成 - t.pieceStates.setState(index, stateFinish) - t.peerPool.release(peer) - log.Debugf("piece %d download success,total:%d,left:%d", index, t.pieceStates.size(), t.pieceStates.getLeft()) - if t.pieceStates.isDone() { - downloadedCh <- nil - } - break - } - } - } - <-pieceQueueCh - }(i) - } - <-downloadedCh - log.Debug("download end") -} diff --git a/pkg/protocol/bt/torrent/torrent_test.go b/pkg/protocol/bt/torrent/torrent_test.go deleted file mode 100644 index 2690ce33..00000000 --- a/pkg/protocol/bt/torrent/torrent_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package torrent - -import ( - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer" - log "github.com/sirupsen/logrus" - "testing" -) - -func init() { - log.SetLevel(log.DebugLevel) -} - -func TestTorrent_Download(t *testing.T) { - torrent := buildTorrent() - torrent.Download("e:/testbt/download") -} - -func buildTorrent() *Torrent { - // metaInfo, err := metainfo.ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") - metaInfo, err := metainfo.ParseFromFile("../testdata/office.torrent") - if err != nil { - panic(err) - } - - metaInfo.AnnounceList[0] = append(metaInfo.AnnounceList[0], []string{ - "udp://tracker.coppersurfer.tk:6969/announce", - "udp://tracker.leechers-paradise.org:6969/announce", - "udp://tracker.opentrackr.org:1337/announce", - "udp://p4p.arenabg.com:1337/announce", - "udp://9.rarbg.to:2710/announce", - "udp://9.rarbg.me:2710/announce", - "udp://tracker.internetwarriors.net:1337/announce", - "udp://exodus.desync.com:6969/announce", - "udp://tracker.tiny-vps.com:6969/announce", - "udp://tracker.sbsub.com:2710/announce", - "udp://retracker.lanta-net.ru:2710/announce", - "udp://open.stealth.si:80/announce", - "udp://open.demonii.si:1337/announce", - "udp://tracker.torrent.eu.org:451/announce", - "udp://tracker.moeking.me:6969/announce", - "udp://tracker.cyberia.is:6969/announce", - "udp://denis.stalker.upeer.me:6969/announce", - "udp://ipv4.tracker.harry.lu:80/announce", - "udp://explodie.org:6969/announce", - "udp://zephir.monocul.us:6969/announce", - "udp://xxxtor.com:2710/announce", - "udp://valakas.rollo.dnsabr.com:2710/announce", - "udp://tracker.zerobytes.xyz:1337/announce", - "udp://tracker.yoshi210.com:6969/announce", - "udp://tracker.uw0.xyz:6969/announce", - "udp://tracker.swateam.org.uk:2710/announce", - "udp://tracker.nyaa.uk:6969/announce", - "udp://tracker.filemail.com:6969/announce", - "udp://tracker.ds.is:6969/announce", - "udp://retracker.sevstar.net:2710/announce", - "udp://retracker.netbynet.ru:2710/announce", - "udp://retracker.akado-ural.ru:80/announce", - "udp://opentracker.i2p.rocks:6969/announce", - "udp://opentor.org:2710/announce", - "udp://open.nyap2p.com:6969/announce", - "udp://chihaya.toss.li:9696/announce", - "udp://bt2.archive.org:6969/announce", - "udp://bt1.archive.org:6969/announce", - "udp://www.loushao.net:8080/announce", - "udp://tracker4.itzmx.com:2710/announce", - "udp://tracker3.itzmx.com:6961/announce", - "udp://tracker2.itzmx.com:6961/announce", - "udp://tracker.lelux.fi:6969/announce", - "udp://tracker.kamigami.org:2710/announce", - "udp://tracker.dler.org:6969/announce", - "udp://tr.bangumi.moe:6969/announce", - "udp://qg.lorzl.gq:2710/announce", - "udp://bt2.54new.com:8080/announce", - }...) - return NewTorrent(peer.GenPeerID(), metaInfo) -} diff --git a/pkg/protocol/bt/tracker/errors.go b/pkg/protocol/bt/tracker/errors.go deleted file mode 100644 index 8d77aa4d..00000000 --- a/pkg/protocol/bt/tracker/errors.go +++ /dev/null @@ -1,29 +0,0 @@ -package tracker - -import ( - "fmt" -) - -type ErrorCode int - -const ( - ErrTimeout ErrorCode = -1 - ErrResponse ErrorCode = -2 -) - -type BtError interface { - error -} - -type TrackerError struct { - Code ErrorCode - Err error -} - -func (te *TrackerError) Error() string { - return fmt.Sprintf("%s:%d", te.Err.Error(), te.Code) -} - -func NewTrackerError(code ErrorCode, err error) *TrackerError { - return &TrackerError{code, err} -} diff --git a/pkg/protocol/bt/tracker/tracker.go b/pkg/protocol/bt/tracker/tracker.go deleted file mode 100644 index 048f832a..00000000 --- a/pkg/protocol/bt/tracker/tracker.go +++ /dev/null @@ -1,412 +0,0 @@ -package tracker - -import ( - "encoding/binary" - "errors" - "fmt" - "io" - "io/ioutil" - "math" - "math/rand" - "net" - "net/http" - "net/url" - "strconv" - "sync" - "time" - - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer" -) - -const ( - udpConnectRequestMagic = 0x41727101980 - udpConnectTimeout = 15 - udpConnectRetries = 3 - udpActionConnect = 0 - udpActionAnnounce = 1 - udpActionScrape = 2 - udpActionError = 3 -) - -/* - Offset size Name Value - 0 64-bit integer protocol_id 0x41727101980 // magic constant - 8 32-bit integer action 0 // connect - 12 32-bit integer transaction_id - 16 - Per https://www.libtorrent.org/udp_tracker_protocol.html#connecting -*/ -type udpConnectRequest struct { - protocolId uint64 - action uint32 - transactionId uint32 -} - -func newUdpConnectRequest() *udpConnectRequest { - return &udpConnectRequest{ - protocolId: udpConnectRequestMagic, - action: udpActionConnect, - transactionId: rand.Uint32(), - } -} - -func (req *udpConnectRequest) encode() []byte { - buf := make([]byte, 16) - binary.BigEndian.PutUint64(buf[:8], req.protocolId) - binary.BigEndian.PutUint32(buf[8:12], req.action) - binary.BigEndian.PutUint32(buf[12:], req.transactionId) - return buf -} - -/* - Offset size Name Value - 0 32-bit integer action 0 // connect - 4 32-bit integer transaction_id - 8 64-bit integer connection_id - 16 -*/ -type udpConnectResponse struct { - action uint32 - transactionId uint32 - connectionId uint64 -} - -func newUdpConnectResponse(buf []byte) *udpConnectResponse { - return &udpConnectResponse{ - action: binary.BigEndian.Uint32(buf[:4]), - transactionId: binary.BigEndian.Uint32(buf[4:8]), - connectionId: binary.BigEndian.Uint64(buf[8:]), - } -} - -/* - Offset size Name Value - 0 64-bit integer connection_id - 8 32-bit integer action 1 // announce - 12 32-bit integer transaction_id - 16 20-byte string info_hash - 36 20-byte string peer_id - 56 64-bit integer downloaded - 64 64-bit integer left - 72 64-bit integer uploaded - 80 32-bit integer event 0 // 0: none; 1: completed; 2: started; 3: stopped - 84 32-bit integer ip address 0 // default - 88 32-bit integer key - 92 32-bit integer num_want -1 // default - 96 16-bit integer port - 98 - Per https://www.libtorrent.org/udp_tracker_protocol.html#announcing -*/ -type udpAnnounceRequest struct { - connectionId uint64 - action uint32 - transactionId uint32 - infoHash [20]byte - peerID [20]byte - downloaded uint64 - left uint64 - uploaded uint64 - event uint32 - ip uint32 - key uint32 - numWant uint32 - port uint16 -} - -func newUdpAnnounceRequest(connectionId uint64) *udpAnnounceRequest { - return &udpAnnounceRequest{ - connectionId: connectionId, - action: udpActionAnnounce, - transactionId: rand.Uint32(), - } -} - -func (req *udpAnnounceRequest) encode() []byte { - buf := make([]byte, 98, 98) - binary.BigEndian.PutUint64(buf[:8], req.connectionId) - binary.BigEndian.PutUint32(buf[8:12], req.action) - binary.BigEndian.PutUint32(buf[12:16], req.transactionId) - copy(buf[16:36], req.infoHash[:]) - copy(buf[36:56], req.peerID[:]) - binary.BigEndian.PutUint64(buf[56:64], req.downloaded) - binary.BigEndian.PutUint64(buf[64:72], req.left) - binary.BigEndian.PutUint64(buf[72:80], req.uploaded) - binary.BigEndian.PutUint32(buf[80:84], req.event) - binary.BigEndian.PutUint32(buf[84:88], req.ip) - binary.BigEndian.PutUint32(buf[88:92], req.key) - binary.BigEndian.PutUint32(buf[92:96], req.numWant) - binary.BigEndian.PutUint16(buf[96:98], req.port) - return buf -} - -/* - Offset size Name Value - 0 32-bit integer action 1 // announce - 4 32-bit integer transaction_id - 8 32-bit integer interval - 12 32-bit integer leechers - 16 32-bit integer seeders - 20 + 6 * n 32-bit integer ip address - 24 + 6 * n 16-bit integer TCP port - 20 + 6 * N - Per https://www.libtorrent.org/udp_tracker_protocol.html#announcing -*/ -type udpAnnounceResponse struct { - action uint32 - transactionId uint32 - interval uint32 - leechers uint32 - seeders uint32 - peers []*peer.Peer -} - -func newUdpAnnounceResponse(buf []byte) *udpAnnounceResponse { - response := &udpAnnounceResponse{ - action: binary.BigEndian.Uint32(buf[:4]), - transactionId: binary.BigEndian.Uint32(buf[4:8]), - interval: binary.BigEndian.Uint32(buf[8:12]), - leechers: binary.BigEndian.Uint32(buf[12:16]), - seeders: binary.BigEndian.Uint32(buf[16:20]), - } - count := (len(buf) - 20) / 6 - response.peers = make([]*peer.Peer, count, count) - for i := 0; i < count; i++ { - ipBegin := 20 + 6*i - portBegin := ipBegin + 4 - response.peers[i] = &peer.Peer{ - IP: binary.BigEndian.Uint32(buf[ipBegin:portBegin]), - Port: binary.BigEndian.Uint16(buf[portBegin : portBegin+2]), - } - } - return response -} - -type Tracker struct { - PeerID [20]byte - MetaInfo *metainfo.MetaInfo -} - -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))) - conn.Write(buf) - len, err := conn.Read(buf) - if err != nil { - return - } - if len != 16 { - err = NewTrackerError(ErrResponse, errors.New("invalid response")) - return - } - response = newUdpConnectResponse(buf) - return -} - -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 - request.downloaded = 0 - request.uploaded = 0 - request.left = tracker.MetaInfo.GetTotalSize() - request.downloaded - request.event = 0 - request.ip = 0 - request.key = 0 - request.numWant = 200 - request.port = 6882 - encode := request.encode() - conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(timeout))) - conn.Write(encode) - buf := make([]byte, 20+request.numWant*6) - len, err := conn.Read(buf) - if err != nil { - return - } - if len < 20 { - err = errors.New("announce fail") - return - } - response = newUdpAnnounceResponse(buf[:len]) - return -} - -// 通过tracker服务器获取peer信息 -func (tracker *Tracker) Tracker() <-chan []*peer.Peer { - peersCh := make(chan []*peer.Peer) - metaInfo := tracker.MetaInfo - - checkAnnounceList := func(announceList [][]string) bool { - if len(announceList) > 0 { - for _, l := range announceList { - if len(l) > 0 { - return true - } - } - } - return false - } - - // 只要获取到了peers就直接返回 - if checkAnnounceList(metaInfo.AnnounceList) { - wg := sync.WaitGroup{} - for _, announceArr := range metaInfo.AnnounceList { - if len(announceArr) > 0 { - for _, announce := range announceArr { - wg.Add(1) - go func(announce string) { - peers, err := tracker.DoTracker(announce) - if err == nil { - peersCh <- peers - } - wg.Done() - }(announce) - } - } - } - go func() { - wg.Wait() - close(peersCh) - }() - } else { - go func() { - peers, err := tracker.DoTracker(metaInfo.Announce) - if err == nil { - peersCh <- peers - } - close(peersCh) - }() - } - return peersCh -} - -func (tracker *Tracker) DoTracker(announce string) (peers []*peer.Peer, err error) { - if announce != "" { - url, _ := url.Parse(announce) - switch url.Scheme { - case "http", "https": - return tracker.httpTracker(url) - case "udp", "udp4", "udp6": - return tracker.udpTracker(url) - default: - return nil, errors.New("unsupported protocol") - } - } - return nil, errors.New("empty announce") -} - -// http://bittorrent.org/beps/bep_0003.html#trackers -func (tracker *Tracker) httpTracker(url *url.URL) (peers []*peer.Peer, err error) { - metaInfo := tracker.MetaInfo - peerID := tracker.PeerID - - query := url.Query() - infoHash := metaInfo.GetInfoHash() - query.Add("info_hash", string(infoHash[:])) - query.Add("peer_id", string(peerID[:])) - query.Add("port", "6882") - query.Add("uploaded", "0") - query.Add("downloaded", "0") - query.Add("left", strconv.FormatInt(int64(metaInfo.GetTotalSize()), 10)) - url.RawQuery = query.Encode() - request, err := http.NewRequest("GET", url.String(), nil) - if err != nil { - return - } - var httpClient = &http.Client{ - Timeout: time.Second * time.Duration(15), - } - request.Header.Set("User-Agent", "gopeed/0.0.1") - response, err := httpClient.Do(request) - if err != nil { - return - } - defer response.Body.Close() - resp, err := ioutil.ReadAll(response.Body) - if err != nil { - if err != io.EOF { - return - } - } - // TODO Parse HTTP tracker response - fmt.Println(resp) - return nil, nil -} - -// http://bittorrent.org/beps/bep_0015.html -func (tracker *Tracker) udpTracker(url *url.URL) (peers []*peer.Peer, err error) { - conn, err := dial(url) - if err != nil { - return - } - defer conn.Close() - - announceResponse, err := func() (announceResponse *udpAnnounceResponse, err error) { - var connectResponse *udpConnectResponse - // 0:connect 1:announce - state := udpActionConnect - // 访问超时最多重试8次,当有请求成功则将重试次数重置为0 - for n := 0; n < udpConnectRetries; n++ { - // 15 * 2 ^ n seconds - timeout := int64(udpConnectTimeout * math.Pow(2, float64(n))) - switch state { - case udpActionConnect: - connectResponse, err = tracker.connect(conn, timeout) - if err != nil { - if oe, ok := err.(*net.OpError); ok && oe.Timeout() { - break - } else { - return - } - } - n = -1 - state = udpActionAnnounce - break - case udpActionAnnounce: - announceResponse, err = tracker.announce(conn, timeout, connectResponse.connectionId) - if err != nil { - if oe, ok := err.(*net.OpError); ok && oe.Timeout() { - break - } else { - return - } - } - return - } - } - return - }() - - if err != nil { - return - } - - return announceResponse.peers, nil -} - -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) { - ips, _ := net.LookupIP(host) - if len(ips) == 0 { - err = errors.New("no ips") - return - } - for _, ip = range ips { - switch scheme { - case "udp", "udp4": - if ip.To4() != nil { - return - } - case "udp6": - if ip.To4() == nil { - return - } - } - } - err = errors.New("no available ips ") - return -} diff --git a/pkg/protocol/bt/tracker/tracker_test.go b/pkg/protocol/bt/tracker/tracker_test.go deleted file mode 100644 index 56e30c98..00000000 --- a/pkg/protocol/bt/tracker/tracker_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package tracker - -import ( - "fmt" - "github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo" - "testing" -) - -func TestTracker_Tracker(t *testing.T) { - metaInfo, err := metainfo.ParseFromFile("../testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent") - if err != nil { - panic(err) - } - tracker := &Tracker{ - PeerID: [20]byte{}, - MetaInfo: metaInfo, - } - tracker.MetaInfo.Announce = "udp://tracker.opentrackr.org:1337/announce" - tracker.MetaInfo.AnnounceList = [][]string{} - peers := <-tracker.Tracker() - fmt.Println(len(peers)) -} diff --git a/pkg/protocol/http/client.go b/pkg/protocol/http/client.go deleted file mode 100644 index 68e17bf1..00000000 --- a/pkg/protocol/http/client.go +++ /dev/null @@ -1,171 +0,0 @@ -package http - -import ( - "fmt" - "io" - "mime" - "net/http" - "net/http/cookiejar" - "os" - "path" - "strconv" - "strings" - "sync" -) - -// Resolve return the file response to be downloaded -func Resolve(request *Request) (*Response, error) { - httpRequest, err := BuildHTTPRequest(request) - if err != nil { - return nil, err - } - // Use "Range" header to resolve - httpRequest.Header.Add("Range", "bytes=0-0") - httpClient := BuildHTTPClient() - response, err := httpClient.Do(httpRequest) - if err != nil { - return nil, err - } - defer response.Body.Close() - if response.StatusCode != 200 && response.StatusCode != 206 { - return nil, fmt.Errorf("response status error:%d", response.StatusCode) - } - ret := &Response{} - // Get file name by "Content-Disposition" - if contentDisposition := response.Header.Get("Content-Disposition"); contentDisposition != "" { - _, params, _ := mime.ParseMediaType(contentDisposition) - if filename := params["filename"]; filename != "" { - ret.Name = filename - } - } - // Get file name by URL - if ret.Name == "" { - ret.Name = path.Base(httpRequest.URL.Path) - } - // Unknown file name - if ret.Name == "" { - ret.Name = "unknown" - } - // Is support range - ret.Range = response.StatusCode == 206 - // Get file size - if ret.Range { - contentRange := response.Header.Get("Content-Range") - if contentRange != "" { - // e.g. bytes 0-1000/1001 => 1001 - total := subLastSlash(contentRange) - if total != "" && total != "*" { - parse, err := strconv.ParseInt(total, 10, 64) - if err != nil { - return nil, err - } - ret.Size = parse - } - } - } else { - contentLength := response.Header.Get("Content-Length") - if contentLength != "" { - ret.Size, _ = strconv.ParseInt(contentLength, 10, 64) - } - } - return ret, nil -} - -// Down -func Down(request *Request) error { - response, err := Resolve(request) - if err != nil { - return err - } - // allocate file - file, err := os.Create(response.Name) - if err != nil { - return err - } - defer file.Close() - if err := file.Truncate(response.Size); err != nil { - return err - } - // support range - if response.Range { - cons := 16 - chunkSize := response.Size / int64(cons) - waitGroup := &sync.WaitGroup{} - waitGroup.Add(cons) - for i := 0; i < cons; i++ { - start := int64(i) * chunkSize - end := start + chunkSize - if i == cons-1 { - end = response.Size - } - go downChunk(request, file, start, end-1, waitGroup) - } - waitGroup.Wait() - } else { - downChunk(request, file, 0, response.Size, nil) - } - return nil -} - -func subLastSlash(str string) string { - index := strings.LastIndex(str, "/") - if index != -1 { - return str[index+1:] - } - return "" -} - -func BuildHTTPRequest(request *Request) (*http.Request, error) { - // Build request - httpRequest, err := http.NewRequest(strings.ToUpper(request.Method), request.URL, nil) - if err != nil { - return nil, err - } - for k, v := range request.Header { - httpRequest.Header.Add(k, v) - } - return httpRequest, nil -} - -func BuildHTTPClient() *http.Client { - // Cookie handle - jar, _ := cookiejar.New(nil) - - return &http.Client{Jar: jar} -} - -func downChunk(request *Request, file *os.File, start int64, end int64, waitGroup *sync.WaitGroup) { - if waitGroup != nil { - defer waitGroup.Done() - } - httpRequest, _ := BuildHTTPRequest(request) - httpRequest.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", start, end)) - fmt.Printf("down %d-%d\n", start, end) - httpClient := BuildHTTPClient() - httpResponse, err := httpClient.Do(httpRequest) - if err != nil { - fmt.Println(err) - return - } - defer httpResponse.Body.Close() - buf := make([]byte, 8192) - writeIndex := start - for { - n, err := httpResponse.Body.Read(buf) - if n > 0 { - writeSize, err := file.WriteAt(buf[0:n], writeIndex) - if err != nil { - fmt.Println(err) - return - } - writeIndex += int64(writeSize) - } - if err != nil { - if err != io.EOF { - fmt.Println(err) - return - } - break - } - } -} diff --git a/pkg/protocol/http/client_test.go b/pkg/protocol/http/client_test.go deleted file mode 100644 index 30f9b6ed..00000000 --- a/pkg/protocol/http/client_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package http - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "io" - "log" - "os" - "reflect" - "testing" - "time" -) - -func TestDown(t *testing.T) { - type args struct { - request *Request - } - tests := []struct { - name string - args args - }{ - { - "proxyee-down.ico", - args{ - &Request{ - "get", - "https://raw.githubusercontent.com/proxyee-down-org/proxyee-down/master/front/public/favicon.ico", - map[string]string{ - "Host": "github.com", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", - "Referer": "http://github.com/proxyee-down-org/proxyee-down/releases", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", - }, - nil, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - os.Remove("favicon.ico") - err := Down(tt.args.request) - if err != nil { - t.Errorf("error down= %v", err) - return - } - downMd5 := fileMd5("favicon.ico") - os.Remove("favicon.ico") - if "8de7a6a2e786861013d61b77b2394012" != downMd5 { - t.Errorf("error md5= %v", downMd5) - return - } - }) - } -} - -func TestResolve(t *testing.T) { - // os.Setenv("HTTP_PROXY", "http://127.0.0.1:8888") - type args struct { - request *Request - } - tests := []struct { - name string - args args - want *Response - wantErr bool - }{ - { - "proxyee-down.ico", - args{ - &Request{ - "get", - "https://raw.githubusercontent.com/proxyee-down-org/proxyee-down/master/front/public/favicon.ico", - map[string]string{ - "Host": "github.com", - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", - "Referer": "http://github.com/proxyee-down-org/proxyee-down/releases", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", - }, - nil, - }, - }, - &Response{"favicon.ico", 116093, true}, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Resolve(tt.args.request) - if (err != nil) != tt.wantErr { - t.Errorf("Resolve() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Resolve() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestTemp(t *testing.T) { - f, err := os.Create("e:/testbt/test.data") - if err != nil { - log.Fatal(err) - } - if err := f.Truncate(1024 * 1024 * 512); err != nil { - log.Fatal(err) - } - bts := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} - s := time.Now().UnixNano() - f.Write(bts) - fmt.Printf("write from 0 use: %d\n", time.Now().UnixNano()-s) - s = time.Now().UnixNano() - f.Seek(1024*1024*128, 0) - f.Write(bts) - fmt.Printf("write from 128M use %d\n", time.Now().UnixNano()-s) - s = time.Now().UnixNano() - f.Write(bts) - fmt.Printf("write from 128M+9b use %d\n", time.Now().UnixNano()-s) - s = time.Now().UnixNano() - f.Seek(0, 0) - f.Write(bts) - fmt.Printf("write from 0 agein use %d\n", time.Now().UnixNano()-s) - s = time.Now().UnixNano() - f.Seek(1024*1024*64, 0) - f.Write(bts) - fmt.Printf("write from agein 64M use %d\n", time.Now().UnixNano()-s) -} - -func fileMd5(filePath string) string { - file, _ := os.Open(filePath) - - // Tell the program to call the following function when the current function returns - defer file.Close() - - // Open a new hash interface to write to - hash := md5.New() - - // Copy the file in the hash interface and check for any error - if _, err := io.Copy(hash, file); err != nil { - return "" - } - return hex.EncodeToString(hash.Sum(nil)) -} - -func TestTemp2(t *testing.T) { - fmt.Println("123456789"[2:3]) -} diff --git a/pkg/protocol/http/request.go b/pkg/protocol/http/request.go deleted file mode 100644 index 45b11372..00000000 --- a/pkg/protocol/http/request.go +++ /dev/null @@ -1,8 +0,0 @@ -package http - -type Request struct { - Method string - URL string - Header map[string]string - Content []byte -} diff --git a/pkg/protocol/http/response.go b/pkg/protocol/http/response.go deleted file mode 100644 index 4bfe9949..00000000 --- a/pkg/protocol/http/response.go +++ /dev/null @@ -1,7 +0,0 @@ -package http - -type Response struct { - Name string - Size int64 - Range bool -}