Also don't bother to read their response bodies.
Result: make(chan RequestResult, 1),
}
go func() {
Result: make(chan RequestResult, 1),
}
go func() {
- b, err := readRequestPartResponses(requestParts)
+ b, err := readRequestPartResponses(ctx, requestParts)
req.Result <- RequestResult{
Bytes: b,
Err: err,
req.Result <- RequestResult{
Bytes: b,
Err: err,
-func recvPartResult(buf io.Writer, part requestPart) error {
+func recvPartResult(ctx context.Context, buf io.Writer, part requestPart) error {
result := <-part.result
if result.err != nil {
return result.err
}
defer result.resp.Body.Close()
result := <-part.result
if result.err != nil {
return result.err
}
defer result.resp.Body.Close()
+ if ctx.Err() != nil {
+ return ctx.Err()
+ }
switch result.resp.StatusCode {
case http.StatusPartialContent:
case http.StatusOK:
switch result.resp.StatusCode {
case http.StatusPartialContent:
case http.StatusOK:
-func readRequestPartResponses(parts []requestPart) ([]byte, error) {
+func readRequestPartResponses(ctx context.Context, parts []requestPart) ([]byte, error) {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
- for _, part := range parts {
- err := recvPartResult(&buf, part)
- if err != nil {
- return buf.Bytes(), fmt.Errorf("reading %q at %q: %w", part.req.URL, part.req.Header.Get("Range"), err)
+ firstErr := make(chan error, 1)
+ go func() {
+ for _, part := range parts {
+ err := recvPartResult(ctx, &buf, part)
+ if err != nil {
+ // Ensure no further unnecessary response reads occur.
+ cancel()
+ select {
+ case firstErr <- fmt.Errorf("reading %q at %q: %w", part.req.URL, part.req.Header.Get("Range"), err):
+ default:
+ }
+ }
- }
- return buf.Bytes(), nil
+ select {
+ case firstErr <- nil:
+ default:
+ }
+ }()
+ // This can't be merged into the return statement, because buf.Bytes is called first!
+ err := <-firstErr
+ return buf.Bytes(), err