9 "github.com/anacrolix/torrent/metainfo"
12 type PathEscaper func(pathComps []string) string
14 // Escapes path name components suitable for appending to a webseed URL. This works for converting
15 // S3 object keys to URLs too.
17 // Contrary to the name, this actually does a QueryEscape, rather than a PathEscape. This works
18 // better with most S3 providers.
19 func EscapePath(pathComps []string) string {
20 return defaultPathEscaper(pathComps)
23 func defaultPathEscaper(pathComps []string) string {
25 for _, comp := range pathComps {
26 esc := url.PathEscape(comp)
27 // S3 incorrectly escapes + in paths to spaces, so we add an extra encoding for that. This
28 // seems to be handled correctly regardless of whether an endpoint uses query or path
30 esc = strings.ReplaceAll(esc, "+", "%2B")
31 ret = append(ret, esc)
33 return strings.Join(ret, "/")
39 pathEscaper PathEscaper,
41 if pathEscaper == nil {
42 pathEscaper = defaultPathEscaper
44 return pathEscaper(append([]string{infoName}, fileComps...))
47 // Creates a request per BEP 19.
49 url_ string, fileIndex int,
52 pathEscaper PathEscaper,
53 ) (*http.Request, error) {
54 fileInfo := info.UpvertedFiles()[fileIndex]
55 if strings.HasSuffix(url_, "/") {
56 // BEP specifies that we append the file path. We need to escape each component of the path
57 // for things like spaces and '#'.
58 url_ += trailingPath(info.Name, fileInfo.Path, pathEscaper)
60 req, err := http.NewRequest(http.MethodGet, url_, nil)
64 if offset != 0 || length != fileInfo.Length {
65 req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+length-1))