diff --git a/client.go b/client.go index df746ecd..ee8261f3 100644 --- a/client.go +++ b/client.go @@ -1326,7 +1326,7 @@ func (t *Torrent) MergeSpec(spec *TorrentSpec) error { defer cl.unlock() t.initialPieceCheckDisabled = spec.DisableInitialPieceCheck for _, url := range spec.Webseeds { - t.addWebSeed(url, spec.DefaultWebseedEscapePath) + t.addWebSeed(url) } for _, peerAddr := range spec.PeerAddrs { t.addPeer(PeerInfo{ diff --git a/spec.go b/spec.go index 6d290e8d..8cce3cb3 100644 --- a/spec.go +++ b/spec.go @@ -6,7 +6,6 @@ import ( "github.com/anacrolix/torrent/metainfo" pp "github.com/anacrolix/torrent/peer_protocol" "github.com/anacrolix/torrent/storage" - "github.com/anacrolix/torrent/webseed" ) // Specifies a new torrent for adding to a client, or additions to an existing Torrent. There are @@ -20,9 +19,11 @@ type TorrentSpec struct { InfoBytes []byte // The name to use if the Name field from the Info isn't available. DisplayName string - Webseeds []string - DhtNodes []string - PeerAddrs []string + // WebSeed URLs. For additional options add the URLs separately with Torrent.AddWebSeeds + // instead. + Webseeds []string + DhtNodes []string + PeerAddrs []string // The combination of the "xs" and "as" fields in magnet links, for now. Sources []string @@ -37,10 +38,6 @@ type TorrentSpec struct { // Whether to allow data download or upload DisallowDataUpload bool DisallowDataDownload bool - - // Custom encoder for webseed URLs - // Leave nil to use the default (url.QueryEscape) - DefaultWebseedEscapePath webseed.PathEscaper } func TorrentSpecFromMagnetUri(uri string) (spec *TorrentSpec, err error) { diff --git a/torrent.go b/torrent.go index a08da702..4cde15ff 100644 --- a/torrent.go +++ b/torrent.go @@ -2350,30 +2350,24 @@ func (t *Torrent) callbacks() *Callbacks { return &t.cl.config.Callbacks } -type AddWebseedsOpt func() *AddWebseedOpts +type AddWebSeedsOpt func(*webseed.Client) -type AddWebseedOpts struct { - // Custom encoder for webseed URLs - // Leave nil to use the default (url.QueryEscape) - PathEscaper webseed.PathEscaper -} - - -// Add web seeds to the torrent. -// If opt is not nil, only the first one is used. -func (t *Torrent) AddWebSeeds(urls []string, opt ...AddWebseedsOpt) { - t.cl.lock() - defer t.cl.unlock() - for _, u := range urls { - if opt == nil { - t.addWebSeed(u, nil) - } else { - t.addWebSeed(u, opt[0]().PathEscaper) - } +// Sets the WebSeed trailing path escaper for a webseed.Client. +func WebSeedPathEscaper(custom webseed.PathEscaper) AddWebSeedsOpt { + return func(c *webseed.Client) { + c.PathEscaper = custom } } -func (t *Torrent) addWebSeed(url string, pathEscaper webseed.PathEscaper) { +func (t *Torrent) AddWebSeeds(urls []string, opts ...AddWebSeedsOpt) { + t.cl.lock() + defer t.cl.unlock() + for _, u := range urls { + t.addWebSeed(u, opts...) + } +} + +func (t *Torrent) addWebSeed(url string, opts ...AddWebSeedsOpt) { if t.cl.config.DisableWebseeds { return } @@ -2410,11 +2404,13 @@ func (t *Torrent) addWebSeed(url string, pathEscaper webseed.PathEscaper) { r: r, } }, - PathEscaper: pathEscaper, }, activeRequests: make(map[Request]webseed.Request, maxRequests), maxRequests: maxRequests, } + for _, opt := range opts { + opt(&ws.client) + } ws.peer.initUpdateRequestsTimer() ws.requesterCond.L = t.cl.locker() for i := 0; i < maxRequests; i += 1 { diff --git a/webseed/client.go b/webseed/client.go index d218cf0e..a86be17a 100644 --- a/webseed/client.go +++ b/webseed/client.go @@ -41,11 +41,6 @@ func (r Request) Cancel() { r.cancel() } -type Spec struct { - Urls []string - EncodeUrl func(string) string -} - type Client struct { HttpClient *http.Client Url string @@ -82,7 +77,7 @@ func (ws *Client) NewRequest(r RequestSpec) Request { ctx, cancel := context.WithCancel(context.Background()) var requestParts []requestPart if !ws.fileIndex.Locate(r, func(i int, e segments.Extent) bool { - req, err := NewRequestWithOpts( + req, err := newRequest( ws.Url, i, ws.info, e.Start, e.Length, ws.PathEscaper, ) diff --git a/webseed/request.go b/webseed/request.go index 460448d0..a38e6372 100644 --- a/webseed/request.go +++ b/webseed/request.go @@ -15,22 +15,13 @@ type PathEscaper func(pathComps []string) string // Escapes path name components suitable for appending to a webseed URL. This works for converting // S3 object keys to URLs too. // -// Contrary to the name, this actually does a QueryEscape, rather than a -// PathEscape. This works better with most S3 providers. You can use -// EscapePathWithOpts for a custom encoding. +// Contrary to the name, this actually does a QueryEscape, rather than a PathEscape. This works +// better with most S3 providers. func EscapePath(pathComps []string) string { - return escapePath(pathComps, nil) + return defaultPathEscaper(pathComps) } -func EscapePathWithCustomEscaper(pathComps []string, pathEscaper PathEscaper) string { - return escapePath(pathComps, pathEscaper) -} - -func escapePath(pathComps []string, pathEscaper PathEscaper) string { - if pathEscaper != nil { - return pathEscaper(pathComps) - } - +func defaultPathEscaper(pathComps []string) string { var ret []string for _, comp := range pathComps { ret = append(ret, url.QueryEscape(comp)) @@ -43,26 +34,13 @@ func trailingPath( fileComps []string, pathEscaper PathEscaper, ) string { - return escapePath(append([]string{infoName}, fileComps...), pathEscaper) + if pathEscaper == nil { + pathEscaper = defaultPathEscaper + } + return pathEscaper(append([]string{infoName}, fileComps...)) } // Creates a request per BEP 19. -func NewRequest( - url_ string, - fileIndex int, info *metainfo.Info, - offset, length int64) (*http.Request, error) { - return newRequest(url_, fileIndex, info, offset, length, nil) -} - -func NewRequestWithOpts( - url_ string, fileIndex int, - info *metainfo.Info, - offset, length int64, - pathEscaper PathEscaper, -) (*http.Request, error) { - return newRequest(url_, fileIndex, info, offset, length, pathEscaper) -} - func newRequest( url_ string, fileIndex int, info *metainfo.Info, @@ -73,7 +51,7 @@ func newRequest( if strings.HasSuffix(url_, "/") { // BEP specifies that we append the file path. We need to escape each component of the path // for things like spaces and '#'. - url_ += escapePath(append([]string{info.Name}, fileInfo.Path...), pathEscaper) + url_ += trailingPath(info.Name, fileInfo.Path, pathEscaper) } req, err := http.NewRequest(http.MethodGet, url_, nil) if err != nil { diff --git a/webseed/request_test.go b/webseed/request_test.go index d39e20e9..7f691e0a 100644 --- a/webseed/request_test.go +++ b/webseed/request_test.go @@ -15,7 +15,7 @@ func TestEscapePath(t *testing.T) { escaper PathEscaper, unescaper func(string) (string, error), ) { - unescaped, err := unescaper(escapePath(parts, escaper)) + unescaped, err := unescaper(escaper(parts)) if !c.Check(err, qt.IsNil) { return } @@ -27,13 +27,13 @@ func TestEscapePath(t *testing.T) { test( []string{"a_b-c", "d + e.f"}, "a_b-c/d + e.f", - nil, + defaultPathEscaper, url.QueryUnescape, ) test( []string{"a_1-b_c2", "d 3. (e, f).g"}, "a_1-b_c2/d 3. (e, f).g", - nil, + defaultPathEscaper, url.QueryUnescape, ) @@ -66,7 +66,7 @@ func TestEscapePath(t *testing.T) { } func TestEscapePathForEmptyInfoName(t *testing.T) { - qt.Check(t, escapePath([]string{`ノ┬─┬ノ ︵ ( \o°o)\`}, nil), qt.Equals, "%E3%83%8E%E2%94%AC%E2%94%80%E2%94%AC%E3%83%8E+%EF%B8%B5+%28+%5Co%C2%B0o%29%5C") - qt.Check(t, escapePath([]string{"hello", "world"}, nil), qt.Equals, "hello/world") - qt.Check(t, escapePath([]string{"war", "and", "peace"}, nil), qt.Equals, "war/and/peace") + qt.Check(t, defaultPathEscaper([]string{`ノ┬─┬ノ ︵ ( \o°o)\`}), qt.Equals, "%E3%83%8E%E2%94%AC%E2%94%80%E2%94%AC%E3%83%8E+%EF%B8%B5+%28+%5Co%C2%B0o%29%5C") + qt.Check(t, defaultPathEscaper([]string{"hello", "world"}), qt.Equals, "hello/world") + qt.Check(t, defaultPathEscaper([]string{"war", "and", "peace"}), qt.Equals, "war/and/peace") }