]> Sergey Matveev's repositories - btrtrc.git/blob - webseed/client.go
Remove events from webseed
[btrtrc.git] / webseed / client.go
1 package webseed
2
3 import (
4         "bytes"
5         "context"
6         "fmt"
7         "io"
8         "net/http"
9
10         "github.com/anacrolix/torrent/metainfo"
11         "github.com/anacrolix/torrent/segments"
12 )
13
14 type RequestSpec = segments.Extent
15
16 type requestPartResult struct {
17         resp *http.Response
18         err  error
19 }
20
21 type requestPart struct {
22         req    *http.Request
23         e      segments.Extent
24         result chan requestPartResult
25 }
26
27 type Request struct {
28         cancel func()
29         Result chan RequestResult
30 }
31
32 func (r Request) Cancel() {
33         r.cancel()
34 }
35
36 type Client struct {
37         HttpClient *http.Client
38         Url        string
39         FileIndex  segments.Index
40         Info       *metainfo.Info
41 }
42
43 type RequestResult struct {
44         Bytes []byte
45         Err   error
46 }
47
48 func (ws *Client) NewRequest(r RequestSpec) Request {
49         ctx, cancel := context.WithCancel(context.Background())
50         var requestParts []requestPart
51         if !ws.FileIndex.Locate(r, func(i int, e segments.Extent) bool {
52                 req, err := NewRequest(ws.Url, i, ws.Info, e.Start, e.Length)
53                 if err != nil {
54                         panic(err)
55                 }
56                 req = req.WithContext(ctx)
57                 part := requestPart{
58                         req:    req,
59                         result: make(chan requestPartResult, 1),
60                         e:      e,
61                 }
62                 go func() {
63                         resp, err := ws.HttpClient.Do(req)
64                         part.result <- requestPartResult{
65                                 resp: resp,
66                                 err:  err,
67                         }
68                 }()
69                 requestParts = append(requestParts, part)
70                 return true
71         }) {
72                 panic("request out of file bounds")
73         }
74         req := Request{
75                 cancel: cancel,
76                 Result: make(chan RequestResult, 1),
77         }
78         go func() {
79                 b, err := readRequestPartResponses(requestParts)
80                 req.Result <- RequestResult{
81                         Bytes: b,
82                         Err:   err,
83                 }
84         }()
85         return req
86 }
87
88 func recvPartResult(buf io.Writer, part requestPart) error {
89         result := <-part.result
90         if result.err != nil {
91                 return result.err
92         }
93         defer result.resp.Body.Close()
94         if part.e.Start != 0 && result.resp.StatusCode != http.StatusPartialContent {
95                 return fmt.Errorf("expected partial content response got %v", result.resp.StatusCode)
96         }
97         copied, err := io.Copy(buf, result.resp.Body)
98         if err != nil {
99                 return err
100         }
101         if copied != part.e.Length {
102                 return fmt.Errorf("got %v bytes, expected %v", copied, part.e.Length)
103         }
104         return nil
105 }
106
107 func readRequestPartResponses(parts []requestPart) ([]byte, error) {
108         var buf bytes.Buffer
109         for _, part := range parts {
110                 err := recvPartResult(&buf, part)
111                 if err != nil {
112                         return buf.Bytes(), err
113                 }
114         }
115         return buf.Bytes(), nil
116 }