2022-02-25 15:38:12 +08:00
|
|
|
package torrent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-04-04 17:13:14 +08:00
|
|
|
"errors"
|
2022-02-25 16:52:23 +08:00
|
|
|
"fmt"
|
2022-02-25 15:38:12 +08:00
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/anacrolix/log"
|
2022-11-15 20:22:10 +08:00
|
|
|
|
2022-02-25 15:38:12 +08:00
|
|
|
"github.com/anacrolix/torrent/bencode"
|
|
|
|
"github.com/anacrolix/torrent/metainfo"
|
|
|
|
)
|
|
|
|
|
2022-03-15 10:42:00 +08:00
|
|
|
// Add HTTP endpoints that serve the metainfo. They will be used if the torrent info isn't obtained
|
|
|
|
// yet. The Client HTTP client is used.
|
|
|
|
func (t *Torrent) UseSources(sources []string) {
|
2022-02-25 15:38:12 +08:00
|
|
|
select {
|
|
|
|
case <-t.Closed():
|
|
|
|
return
|
|
|
|
case <-t.GotInfo():
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
for _, s := range sources {
|
|
|
|
_, loaded := t.activeSources.LoadOrStore(s, struct{}{})
|
|
|
|
if loaded {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
s := s
|
|
|
|
go func() {
|
|
|
|
err := t.useActiveTorrentSource(s)
|
|
|
|
_, loaded := t.activeSources.LoadAndDelete(s)
|
|
|
|
if !loaded {
|
|
|
|
panic(s)
|
|
|
|
}
|
|
|
|
level := log.Debug
|
2023-04-04 17:13:14 +08:00
|
|
|
if err != nil && !errors.Is(err, context.Canceled) {
|
2022-02-25 15:38:12 +08:00
|
|
|
level = log.Warning
|
|
|
|
}
|
2022-02-25 16:51:11 +08:00
|
|
|
t.logger.Levelf(level, "used torrent source %q [err=%v]", s, err)
|
2022-02-25 15:38:12 +08:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Torrent) useActiveTorrentSource(source string) error {
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
go func() {
|
|
|
|
select {
|
|
|
|
case <-t.GotInfo():
|
|
|
|
case <-t.Closed():
|
|
|
|
case <-ctx.Done():
|
|
|
|
}
|
|
|
|
cancel()
|
|
|
|
}()
|
|
|
|
mi, err := getTorrentSource(ctx, source, t.cl.httpClient)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return t.MergeSpec(TorrentSpecFromMetaInfo(&mi))
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTorrentSource(ctx context.Context, source string, hc *http.Client) (mi metainfo.MetaInfo, err error) {
|
|
|
|
var req *http.Request
|
|
|
|
if req, err = http.NewRequestWithContext(ctx, http.MethodGet, source, nil); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var resp *http.Response
|
|
|
|
if resp, err = hc.Do(req); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
2022-02-25 16:52:23 +08:00
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
err = fmt.Errorf("unexpected response status code: %v", resp.StatusCode)
|
|
|
|
return
|
|
|
|
}
|
2022-02-25 15:38:12 +08:00
|
|
|
err = bencode.NewDecoder(resp.Body).Decode(&mi)
|
|
|
|
return
|
|
|
|
}
|