]> Sergey Matveev's repositories - btrtrc.git/blob - webseed/request.go
460448d08a0c3d240cc51a432ef08d2dc91acf4e
[btrtrc.git] / webseed / request.go
1 package webseed
2
3 import (
4         "fmt"
5         "net/http"
6         "net/url"
7         "path"
8         "strings"
9
10         "github.com/anacrolix/torrent/metainfo"
11 )
12
13 type PathEscaper func(pathComps []string) string
14
15 // Escapes path name components suitable for appending to a webseed URL. This works for converting
16 // S3 object keys to URLs too.
17 //
18 // Contrary to the name, this actually does a QueryEscape, rather than a
19 // PathEscape. This works better with most S3 providers. You can use
20 // EscapePathWithOpts for a custom encoding.
21 func EscapePath(pathComps []string) string {
22         return escapePath(pathComps, nil)
23 }
24
25 func EscapePathWithCustomEscaper(pathComps []string, pathEscaper PathEscaper) string {
26         return escapePath(pathComps, pathEscaper)
27 }
28
29 func escapePath(pathComps []string, pathEscaper PathEscaper) string {
30         if pathEscaper != nil {
31                 return pathEscaper(pathComps)
32         }
33
34         var ret []string
35         for _, comp := range pathComps {
36                 ret = append(ret, url.QueryEscape(comp))
37         }
38         return path.Join(ret...)
39 }
40
41 func trailingPath(
42         infoName string,
43         fileComps []string,
44         pathEscaper PathEscaper,
45 ) string {
46         return escapePath(append([]string{infoName}, fileComps...), pathEscaper)
47 }
48
49 // Creates a request per BEP 19.
50 func NewRequest(
51         url_ string,
52         fileIndex int, info *metainfo.Info,
53         offset, length int64) (*http.Request, error) {
54         return newRequest(url_, fileIndex, info, offset, length, nil)
55 }
56
57 func NewRequestWithOpts(
58         url_ string, fileIndex int,
59         info *metainfo.Info,
60         offset, length int64,
61         pathEscaper PathEscaper,
62 ) (*http.Request, error) {
63         return newRequest(url_, fileIndex, info, offset, length, pathEscaper)
64 }
65
66 func newRequest(
67         url_ string, fileIndex int,
68         info *metainfo.Info,
69         offset, length int64,
70         pathEscaper PathEscaper,
71 ) (*http.Request, error) {
72         fileInfo := info.UpvertedFiles()[fileIndex]
73         if strings.HasSuffix(url_, "/") {
74                 // BEP specifies that we append the file path. We need to escape each component of the path
75                 // for things like spaces and '#'.
76                 url_ += escapePath(append([]string{info.Name}, fileInfo.Path...), pathEscaper)
77         }
78         req, err := http.NewRequest(http.MethodGet, url_, nil)
79         if err != nil {
80                 return nil, err
81         }
82         if offset != 0 || length != fileInfo.Length {
83                 req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+length-1))
84         }
85         return req, nil
86 }