11 "github.com/anacrolix/torrent/common"
12 "github.com/anacrolix/torrent/metainfo"
13 pp "github.com/anacrolix/torrent/peer_protocol"
14 "github.com/anacrolix/torrent/segments"
15 "github.com/anacrolix/torrent/webseed"
18 type webseedPeer struct {
20 activeRequests map[Request]webseed.Request
21 requesterCond sync.Cond
25 var _ peerImpl = (*webseedPeer)(nil)
27 func (me *webseedPeer) connStatusString() string {
31 func (ws *webseedPeer) String() string {
32 return fmt.Sprintf("webseed peer for %q", ws.client.Url)
35 func (ws *webseedPeer) onGotInfo(info *metainfo.Info) {
36 ws.client.FileIndex = segments.NewIndex(common.LengthIterFromUpvertedFiles(info.UpvertedFiles()))
40 func (ws *webseedPeer) _postCancel(r Request) {
44 func (ws *webseedPeer) writeInterested(interested bool) bool {
48 func (ws *webseedPeer) cancel(r Request) bool {
49 active, ok := ws.activeRequests[r]
57 func (ws *webseedPeer) intoSpec(r Request) webseed.RequestSpec {
58 return webseed.RequestSpec{ws.peer.t.requestOffset(r), int64(r.Length)}
61 func (ws *webseedPeer) request(r Request) bool {
62 ws.requesterCond.Signal()
66 func (ws *webseedPeer) doRequest(r Request) {
67 webseedRequest := ws.client.NewRequest(ws.intoSpec(r))
68 ws.activeRequests[r] = webseedRequest
70 ws.requesterCond.L.Unlock()
71 defer ws.requesterCond.L.Lock()
72 ws.requestResultHandler(r, webseedRequest)
74 delete(ws.activeRequests, r)
77 func (ws *webseedPeer) requester() {
78 ws.requesterCond.L.Lock()
79 defer ws.requesterCond.L.Unlock()
81 for !ws.peer.closed.IsSet() {
82 for r := range ws.peer.requests {
83 if _, ok := ws.activeRequests[r]; ok {
89 ws.requesterCond.Wait()
93 func (ws *webseedPeer) connectionFlags() string {
97 // TODO: This is called when banning peers. Perhaps we want to be able to ban webseeds too. We could
98 // return bool if this is even possible, and if it isn't, skip to the next drop candidate.
99 func (ws *webseedPeer) drop() {}
101 func (ws *webseedPeer) updateRequests() {
102 ws.peer.doRequestState()
105 func (ws *webseedPeer) onClose() {
106 ws.peer.logger.Print("closing")
107 for _, r := range ws.activeRequests {
110 ws.requesterCond.Broadcast()
113 func (ws *webseedPeer) requestResultHandler(r Request, webseedRequest webseed.Request) {
114 result := <-webseedRequest.Result
116 defer ws.peer.t.cl.unlock()
117 if result.Err != nil {
118 if !errors.Is(result.Err, context.Canceled) {
119 ws.peer.logger.Printf("Request %v rejected: %v", r, result.Err)
121 // We need to filter out temporary errors, but this is a nightmare in Go. Currently a bad
122 // webseed URL can starve out the good ones due to the chunk selection algorithm.
123 const closeOnAllErrors = false
124 if closeOnAllErrors ||
125 strings.Contains(result.Err.Error(), "unsupported protocol scheme") ||
127 var err webseed.ErrBadResponse
128 if !errors.As(result.Err, &err) {
131 return err.Response.StatusCode == http.StatusNotFound
135 ws.peer.remoteRejectedRequest(r)
138 err := ws.peer.receiveChunk(&pp.Message{