package torrent import ( "context" "errors" "fmt" "net/http" "github.com/anacrolix/log" "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/metainfo" ) // 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) { 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 if err != nil && !errors.Is(err, context.Canceled) { level = log.Warning } t.logger.Levelf(level, "used torrent source %q [err=%v]", s, err) }() } } 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() if resp.StatusCode != http.StatusOK { err = fmt.Errorf("unexpected response status code: %v", resp.StatusCode) return } err = bencode.NewDecoder(resp.Body).Decode(&mi) return }