Skip to content

Instantly share code, notes, and snippets.

@xeoncross
Created November 5, 2018 15:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xeoncross/666e448c9edea957b76b653f3d6215d9 to your computer and use it in GitHub Desktop.
Save xeoncross/666e448c9edea957b76b653f3d6215d9 to your computer and use it in GitHub Desktop.
package phttp
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"golang.org/x/net/context/ctxhttp"
"golang.org/x/sync/errgroup"
)
func PGet(ctx context.Context, client *http.Client, w io.WriterAt, n int, url string) error {
resp, err := ctxhttp.Head(ctx, client, url)
if err != nil {
return err
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("server responded with %d status code", resp.StatusCode)
}
if resp.Header.Get("Accept-Ranges") != "bytes" {
return errors.New("server does not support range requests")
}
if resp.ContentLength < 0 {
return errors.New("server sent invalid Content-Length header")
}
sectionLen := resp.ContentLength / int64(n)
wg, ctx := errgroup.WithContext(ctx)
for off := int64(0); off < resp.ContentLength; off += sectionLen {
off := off
lim := off + sectionLen
if lim >= resp.ContentLength {
lim = resp.ContentLength
}
wg.Go(func() error { return getPart(ctx, client, w, url, off, lim) })
}
return wg.Wait()
}
func getPart(ctx context.Context, client *http.Client, w io.WriterAt, url string, off, lim int64) error {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, lim))
resp, err := ctxhttp.Do(ctx, client, req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusPartialContent {
return fmt.Errorf("server responded with %d status code, expected %d", resp.StatusCode, http.StatusPartialContent)
}
_, err = io.Copy(newSectionWriter(w, off), resp.Body)
return err
}
func newSectionWriter(w io.WriterAt, off int64) *sectionWriter {
return &sectionWriter{w, off}
}
type sectionWriter struct {
w io.WriterAt
off int64
}
func (w *sectionWriter) Write(p []byte) (n int, err error) {
n, err = w.w.WriteAt(p, w.off)
w.off += int64(n)
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment