-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
278 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package model | ||
|
||
import "code.gopub.tech/bencode" | ||
|
||
type File bencode.Dict | ||
|
||
// Length 文件大小(字节数) | ||
func (f File) Length() int64 { | ||
return bencode.AsInt(f["length"]) | ||
} | ||
|
||
// Path 路径 | ||
func (f File) Path() (path []string) { | ||
list := bencode.AsList(f["path"]) | ||
for _, item := range list { | ||
if p, ok := item.(bencode.String); ok { | ||
path = append(path, string(p)) | ||
} | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package model | ||
|
||
import ( | ||
"crypto/sha1" | ||
"fmt" | ||
"net/url" | ||
|
||
"code.gopub.tech/bencode" | ||
) | ||
|
||
// Info | ||
type Info bencode.Dict | ||
|
||
// Name 建议保存为的文件(夹)名 实际保存时用户也可另行指定 | ||
func (i Info) Name() string { | ||
return bencode.AsStr(i["name"]) | ||
} | ||
|
||
// PieceLength 一个分片的长度(字节数) 通常是 2 的幂次方 | ||
func (i Info) PieceLength() int64 { | ||
return bencode.AsInt(i["piece length"]) | ||
} | ||
|
||
// Pieces 长度是 20 的整数倍; 每 20 位表示一个分片的 SHA1 散列值 | ||
func (i Info) Pieces() []byte { | ||
return []byte(bencode.AsString(i["pieces"])) | ||
} | ||
|
||
// IsPrivate 是否是私有种子(只能通过追踪器获取对等方信息) | ||
// see bep_0027 | ||
func (i Info) IsPrivate() bool { | ||
return bencode.AsInt(i["private"]) == 1 | ||
} | ||
|
||
// IsSingleFile 是否是单文件 | ||
func (i Info) IsSingleFile() bool { | ||
_, ok := i["length"] | ||
return ok | ||
} | ||
|
||
// Length 如果是单文件 表示文件大小(字节数) | ||
func (i Info) Length() int64 { | ||
return bencode.AsInt(i["length"]) | ||
} | ||
|
||
// IsMultiFile 是否是多文件 | ||
func (i Info) IsMultiFile() bool { | ||
_, ok := i["files"] | ||
return ok | ||
} | ||
|
||
// Files 如果是多文件 表示目录下的文件 | ||
func (i Info) Files() (files []File) { | ||
for _, file := range bencode.AsList(i["files"]) { | ||
if f, ok := file.(bencode.Dict); ok { | ||
files = append(files, File(f)) | ||
} | ||
} | ||
return | ||
} | ||
|
||
func (i Info) TotalSize() int64 { | ||
if i.IsSingleFile() { | ||
return i.Length() | ||
} | ||
var sum int64 | ||
for _, file := range i.Files() { | ||
sum += file.Length() | ||
} | ||
return sum | ||
} | ||
|
||
// Hash 种子文件的 info_hash 用这个哈希值来识别一个种子 | ||
func (i Info) Hash() []byte { | ||
info := bencode.Dict(i).Encode() | ||
hash := sha1.Sum(info) | ||
return hash[:] | ||
} | ||
|
||
// HashStr 种子文件的 info_hash 的十六进制表示 | ||
func (i Info) HashStr() string { | ||
return fmt.Sprintf("%x", i.Hash()) | ||
} | ||
|
||
// HashEscape 种子文件的 info_hash 用在 url 上的表示(使用百分号转义) | ||
func (i Info) HashEscape() string { | ||
return url.QueryEscape(string(i.Hash())) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,105 @@ | ||
package model | ||
|
||
import ( | ||
"math/rand" | ||
"time" | ||
|
||
"code.gopub.tech/bencode" | ||
) | ||
|
||
type Meta bencode.Dict | ||
// MetaInfo 种子文件 | ||
// see bep_0003 | ||
type MetaInfo bencode.Dict | ||
|
||
func (m Meta) Announce() string { | ||
// Announce 追踪器的地址 (一定有的字段) | ||
func (m MetaInfo) Announce() string { | ||
return bencode.AsStr(m["announce"]) | ||
} | ||
|
||
func (m Meta) AnnounceList() (list []string) { | ||
for _, item := range bencode.AsList(m["announce-list"]) { | ||
if l := bencode.AsList(item); len(l) == 1 { | ||
if s, ok := l[0].(bencode.String); ok { | ||
list = append(list, string(s)) | ||
} | ||
} | ||
} | ||
return | ||
// Info 描述种子文件对应的文件(夹) (一定有的字段) | ||
func (m MetaInfo) Info() Info { | ||
return Info(bencode.AsDict(m["info"])) | ||
} | ||
|
||
func (m Meta) Comment() string { | ||
// Comment 注释 | ||
func (m MetaInfo) Comment() string { | ||
return bencode.AsStr(m["comment"]) | ||
} | ||
|
||
func (m Meta) CreationDate() int64 { | ||
// CreationDate 创建时间 秒级时间戳 | ||
func (m MetaInfo) CreationDate() int64 { | ||
return bencode.AsInt(m["creation date"]) | ||
} | ||
|
||
func (m Meta) CreationDateTime() time.Time { | ||
// CreationDateTime 创建时间 | ||
func (m MetaInfo) CreationDateTime() time.Time { | ||
sec := m.CreationDate() | ||
return time.Unix(sec, 0) | ||
} | ||
|
||
func (m Meta) Info() Info { | ||
return Info(bencode.AsDict(m["info"])) | ||
} | ||
|
||
type Info bencode.Dict | ||
|
||
func (i Info) Name() string { | ||
return bencode.AsStr(i["name"]) | ||
} | ||
|
||
func (i Info) PieceLength() int64 { | ||
return bencode.AsInt(i["piece length"]) | ||
} | ||
|
||
func (i Info) Files() (files []File) { | ||
for _, file := range bencode.AsList(i["files"]) { | ||
if f, ok := file.(bencode.Dict); ok { | ||
files = append(files, File(f)) | ||
// AnnounceList 扩展后的追踪器列表 | ||
// 如果客户端兼容该字段 应当优先使用这个字段, 并且忽略 announce 字段. | ||
// | ||
// 各层级的通告将按顺序处理;在客户端进入下一层级之前,必须检查每一层级中的所有 URL。 | ||
// 每一层级内的 URL 将以随机选择的顺序进行处理;换句话说,首次读取时列表将被打乱,然后按顺序进行解析。 | ||
// 此外,如果与跟踪器的连接成功,它将被移到该层级的前面。 | ||
// | ||
// 示例 | ||
// | ||
// d['announce-list'] = [ [tracker1], [backup1], [backup2] ] | ||
// 每次通告时,首先尝试 追踪器 1, 如果无法联系到 追踪器 1, 则分别尝试 备份 1 和 备份 2. | ||
// 在下一次通告时,按同样的顺序重复。这适用于标准跟踪器无法共享信息的情况。 | ||
// | ||
// d['announce-list'] = [[ tracker1, tracker2, tracker3 ]] | ||
// 首先,打乱列表。(为便于讨论,我们假设列表已经被打乱。) | ||
// 然后,如果无法访问 tracker1, 则尝试 tracker2. | ||
// 如果可以访问 tracker2, 那么列表现在是:tracker2, tracker1, tracker3. | ||
// 从那时起,这将是客户端尝试的顺序。 | ||
// 如果稍后 tracker2 和 tracker1 都无法访问,但 tracker3 有响应, | ||
// 那么列表将更改为:tracker3, tracker2, tracker1, 并在未来按照该顺序进行尝试。 | ||
// 这种形式适用于可以交换对等体信息的跟踪器,并会使客户端帮助平衡跟踪器之间的负载。 | ||
// | ||
// d['announce-list'] = [ [ tracker1, tracker2 ], [backup1] ] | ||
// 第一层由 tracker1 和 tracker2 组成,是随机排列的。 | ||
// 在客户端尝试连接 backup1 之前,每次通告都会尝试 tracker1 和 tracker2(尽管顺序可能不同)。 | ||
// | ||
// see bep_0012 | ||
func (m MetaInfo) AnnounceList() (trackers [][]string) { | ||
list := bencode.AsList(m["announce-list"]) | ||
trackers = make([][]string, 0, len(list)) | ||
for _, tiers := range list { | ||
vals := bencode.AsList(tiers) | ||
urls := make([]string, 0, len(vals)) | ||
for _, url := range vals { | ||
urls = append(urls, bencode.AsStr(url)) | ||
} | ||
trackers = append(trackers, urls) | ||
} | ||
return | ||
} | ||
|
||
type File bencode.Dict | ||
|
||
func (f File) Length() int64 { | ||
return bencode.AsInt(f["length"]) | ||
} | ||
|
||
func (f File) Path() (path []string) { | ||
list := bencode.AsList(f["path"]) | ||
for _, item := range list { | ||
if p, ok := item.(bencode.String); ok { | ||
path = append(path, string(p)) | ||
// Trackers 获取追踪器列表 | ||
func (m MetaInfo) Trackers() *Trackers { | ||
list := m.AnnounceList() | ||
announceList := make([][]string, 0, len(list)) | ||
for _, tiers := range list { | ||
urls := make([]string, 0, len(tiers)) | ||
for _, url := range tiers { | ||
if url != "" { | ||
urls = append(urls, url) | ||
} | ||
} | ||
if length := len(urls); length > 0 { | ||
rand.Shuffle(int(int32(length)), func(i, j int) { | ||
urls[i], urls[j] = urls[j], urls[i] | ||
}) | ||
announceList = append(announceList, urls) | ||
} | ||
} | ||
return | ||
if len(announceList) == 0 { | ||
announceList = append(announceList, []string{m.Announce()}) | ||
} | ||
return &Trackers{ | ||
AnnounceList: announceList, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package model | ||
|
||
// Trackers 追踪器列表 | ||
type Trackers struct { | ||
AnnounceList [][]string | ||
// [ [a, b, c], [back1, back2] ] | ||
// tiers 表示等级索引 当前使用第一等级 [a,b,c] 还是第二等级[back1, back2] | ||
// index 表示当前等级追踪器索引 | ||
tiers, index int | ||
} | ||
|
||
func (t *Trackers) Reset() { | ||
t.tiers, t.index = 0, 0 | ||
} | ||
|
||
func (t *Trackers) Tiers() int { | ||
return t.tiers | ||
} | ||
|
||
func (t *Trackers) Next() string { | ||
return t.AnnounceList[t.tiers][t.index] | ||
} | ||
|
||
func (t *Trackers) MarkCurrentFail() { | ||
tiers := t.AnnounceList[t.tiers] | ||
t.index++ | ||
if t.index >= len(tiers) { | ||
t.tiers++ | ||
t.tiers = t.tiers % len(t.AnnounceList) | ||
t.index = 0 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package model_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"code.gopub.tech/assert" | ||
"code.gopub.tech/gbt/model" | ||
) | ||
|
||
func TestTrackers(t *testing.T) { | ||
var trackers = &model.Trackers{ | ||
AnnounceList: [][]string{ | ||
{"tracker1", "tracker2"}, | ||
{"backup1", "backup2"}, | ||
}, | ||
} | ||
assert.Equal(t, trackers.Next(), "tracker1") | ||
trackers.MarkCurrentFail() | ||
assert.Equal(t, trackers.Next(), "tracker2") | ||
trackers.MarkCurrentFail() | ||
assert.Equal(t, trackers.Next(), "backup1") | ||
trackers.MarkCurrentFail() | ||
assert.Equal(t, trackers.Next(), "backup2") | ||
trackers.MarkCurrentFail() | ||
assert.Equal(t, trackers.Next(), "tracker1") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.