Chonker speeds up large file downloads from S3 by fetching files in chunks using HTTP range requests.
wot it be
DDOS-ing your upstream file server is generally not a good idea. AWS S3’s horizontal scaling design essentially mandates that you use parallel network requests to fully saturate available bandwidth. To that end, the AWS CLI itself makes ten parallel requests to S3 out of the box. Chonker brings that same experience to your Go programs.
Chonker is a rewrite
of a now non-existent project called ranger written by the venerable
@sudhirj.
It doesn’t exist there for boring $job
reasons,
but the original code lives on in Chonker’s git history.
The standard Go HTTP request response API lives on in Chonker whereby
a single HTTP request yields a single HTTP response object.
Clients read off this response’s body blissfully unaware that
chonker is fetching chunks and piping them to the io.Reader
in order.
vroooom
Go made creating Chonker an absolute delight.
An io.Reader
is connected to an io.Writer
interface
by an io.Pipe
. Chunk download goroutines fetch chunks
quickly and potentially out-of-order.
Writes to the pipe happen in-order ensuring that clients
see the right file.
Faking a http.Response
for the entire file instead of
each chunk is straight forward as well.
how to use
import (
"net/http"
"github.com/ananthb/chonker"
)
func main() {
// Create a new Chonker client
client := chonker.NewClient()
// Create a new HTTP request
req, _ := http.NewRequest("GET", "https://example.com/file", nil)
// Fetch the file
resp, _ := client.Do(req)
// Read the file
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Chonker ends up being a drop-in replacement in any code that
expects a http.Client
. If you’re not pointing your clients
at S3/CloudFront, make sure that your backend server is okay
with clients opening more than one connection to fetch the same file.
AWS CloudFront Weirdness
AWS CloudFront may be at odds with IETF RFC 7231 Section 4.3.2 which states that servers should response with HTTP HEAD requests as if they were get requests. The server just has to omit the response body in this case.
If you have a file in CloudFront larger than 25GiB, then GET requests unadorned
with a Range
header will fail with a 400 error. This has the side effect of
also making HEAD requests fail.
Chonker works around this by always sending a Range
header with a 0-0 range
for the first discovery request, but this leaves a strange feeling in my gut.
I want my HEAD requests dammit!