go 42 lines · 8 steps

Streaming a proxied download in Gin

A Gin handler fetches a file from upstream storage and streams it straight back to the client, preserving range requests and key headers.

Explained by highlit
1func ProxyDownload(c *gin.Context) {
2 objectKey := c.Param("key")
3 upstream := fmt.Sprintf("%s/files/%s", storageBaseURL, objectKey)
4 
5 req, err := http.NewRequestWithContext(c.Request.Context(), http.MethodGet, upstream, nil)
6 if err != nil {
7 c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "failed to build upstream request"})
8 return
9 }
10 
11 if rng := c.GetHeader("Range"); rng != "" {
12 req.Header.Set("Range", rng)
13 }
14 req.Header.Set("Authorization", "Bearer "+storageToken)
15 
16 resp, err := httpClient.Do(req)
17 if err != nil {
18 c.AbortWithStatusJSON(http.StatusBadGateway, gin.H{"error": "upstream unreachable"})
19 return
20 }
21 defer resp.Body.Close()
22 
23 if resp.StatusCode >= http.StatusBadRequest {
24 c.AbortWithStatusJSON(resp.StatusCode, gin.H{"error": "upstream returned an error"})
25 return
26 }
27 
28 extraHeaders := map[string]string{}
29 if cd := resp.Header.Get("Content-Disposition"); cd != "" {
30 extraHeaders["Content-Disposition"] = cd
31 }
32 if ar := resp.Header.Get("Accept-Ranges"); ar != "" {
33 extraHeaders["Accept-Ranges"] = ar
34 }
35 
36 contentType := resp.Header.Get("Content-Type")
37 if contentType == "" {
38 contentType = "application/octet-stream"
39 }
40 
41 c.DataFromReader(resp.StatusCode, resp.ContentLength, contentType, resp.Body, extraHeaders)
42}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Tying the outbound request to the incoming request's context lets client disconnects cancel upstream work automatically.
  2. 2Streaming from the response body with DataFromReader avoids buffering the whole file in memory.
  3. 3A proxy should forward meaningful headers like Range and Content-Disposition so downstream behavior stays intact.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Streaming a proxied download in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code