sources.go 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package torrent
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "github.com/anacrolix/log"
  8. "github.com/anacrolix/torrent/bencode"
  9. "github.com/anacrolix/torrent/metainfo"
  10. )
  11. // Add HTTP endpoints that serve the metainfo. They will be used if the torrent info isn't obtained
  12. // yet. The Client HTTP client is used.
  13. func (t *Torrent) UseSources(sources []string) {
  14. select {
  15. case <-t.Closed():
  16. return
  17. case <-t.GotInfo():
  18. return
  19. default:
  20. }
  21. for _, s := range sources {
  22. _, loaded := t.activeSources.LoadOrStore(s, struct{}{})
  23. if loaded {
  24. continue
  25. }
  26. s := s
  27. go func() {
  28. err := t.useActiveTorrentSource(s)
  29. _, loaded := t.activeSources.LoadAndDelete(s)
  30. if !loaded {
  31. panic(s)
  32. }
  33. level := log.Debug
  34. if err != nil && !errors.Is(err, context.Canceled) {
  35. level = log.Info
  36. }
  37. t.logger.Levelf(level, "used torrent source %q [err=%v]", s, err)
  38. }()
  39. }
  40. }
  41. func (t *Torrent) useActiveTorrentSource(source string) error {
  42. ctx, cancel := context.WithCancel(context.Background())
  43. defer cancel()
  44. go func() {
  45. select {
  46. case <-t.GotInfo():
  47. case <-t.Closed():
  48. case <-ctx.Done():
  49. }
  50. cancel()
  51. }()
  52. mi, err := getTorrentSource(ctx, source, t.cl.httpClient)
  53. if err != nil {
  54. return err
  55. }
  56. return t.MergeSpec(TorrentSpecFromMetaInfo(&mi))
  57. }
  58. func getTorrentSource(ctx context.Context, source string, hc *http.Client) (mi metainfo.MetaInfo, err error) {
  59. var req *http.Request
  60. if req, err = http.NewRequestWithContext(ctx, http.MethodGet, source, nil); err != nil {
  61. return
  62. }
  63. var resp *http.Response
  64. if resp, err = hc.Do(req); err != nil {
  65. return
  66. }
  67. defer resp.Body.Close()
  68. if resp.StatusCode != http.StatusOK {
  69. err = fmt.Errorf("unexpected response status code: %v", resp.StatusCode)
  70. return
  71. }
  72. err = bencode.NewDecoder(resp.Body).Decode(&mi)
  73. return
  74. }