This repository was archived by the owner on Feb 12, 2024. It is now read-only.
forked from ananthb/chonker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_client.go
107 lines (90 loc) · 2.77 KB
/
http_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package ranger
import (
"errors"
"fmt"
"io"
"net/http"
"strconv"
"strings"
)
type roundTripper struct {
ranger Ranger
client *http.Client
rscGen RSCGen
}
func (s roundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
//if the request is not a GET request, we don't want to do anything special, just delegate to the default client
if request.Method != http.MethodGet {
return s.client.Do(request)
}
lengthReq, err := http.NewRequest("GET", request.URL.String(), nil)
if err != nil {
return nil, err
}
lengthReq.Header.Set("Range", ByteRange{From: 0, To: 0}.RangeHeader())
lengthResp, err := s.client.Do(lengthReq)
if err != nil {
return nil, err
}
lengthRespRangeHeaderParts := strings.Split(lengthResp.Header.Get("Content-Range"), "/")
if len(lengthRespRangeHeaderParts) < 2 {
return nil, errors.New("could not figure out content length from Content-Range header")
}
totalContentLength, err := strconv.ParseInt(lengthRespRangeHeaderParts[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("unable to get content length: %w", err)
}
parsedRange, err := ParseRange(request.Header.Get("Range"), totalContentLength)
if err != nil || len(parsedRange) > 1 {
return nil, fmt.Errorf("unable to parse requested Range header correctly: %w", err)
}
reader := s.rscGen(s.client, request.URL.String(), NewSizedRanger(totalContentLength, s.ranger))
rangeToFetch := Range{
Start: 0,
Length: totalContentLength,
}
if parsedRange != nil {
rangeToFetch = parsedRange[0]
}
_, err = reader.Seek(rangeToFetch.Start, io.SeekStart)
if err != nil {
return nil, fmt.Errorf("unable to seek correctly: %w", err)
}
headers := lengthResp.Header.Clone()
headers.Set("Content-Range", rangeToFetch.ContentRange(totalContentLength))
limitReader := io.LimitReader(reader, rangeToFetch.Length)
body := readCloser{limitReader, reader}
return &http.Response{
Status: lengthResp.Status,
StatusCode: lengthResp.StatusCode,
Proto: lengthResp.Proto,
ProtoMajor: lengthResp.ProtoMajor,
ProtoMinor: lengthResp.ProtoMinor,
Header: headers,
Body: body,
ContentLength: rangeToFetch.Length,
Close: true,
Request: request,
TLS: lengthResp.TLS,
}, nil
}
type RSCGen func(client *http.Client, url string, sr SizedRanger) io.ReadSeekCloser
type readCloser struct {
r io.Reader
c io.Closer
}
func (rc readCloser) Read(p []byte) (n int, err error) {
return rc.r.Read(p)
}
func (rc readCloser) Close() error {
return rc.c.Close()
}
func NewSeqRangingClient(ranger Ranger, client *http.Client) http.RoundTripper {
return &roundTripper{
ranger: ranger,
client: client,
rscGen: func(client *http.Client, url string, sr SizedRanger) io.ReadSeekCloser {
return NewSeqReader(client, url, sr)
},
}
}