gin/render/reader.go
Alex 50c29053dd Fix concurrent write bug in reader.Render
If a caller passed in a map retained within the callers context as
extraHeaders to gin.Context.DataFromReader() then a race to write the
"Content-Lenght" header would occur.

// globalHeader is passed to gin.Context.DataFromReader
var globalHeaders = map[string]string{
    "cache-control": "public, max-age=3600",
}

func (c *gin.Context) {
    //...

    // DataFromReader must not write to globalHeaders
    c.DataFromReader(code, contentLength, contentType, reader,
	globalHeaders)
}
2023-04-12 14:24:37 +02:00

48 lines
1.1 KiB
Go

// Copyright 2018 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package render
import (
"io"
"net/http"
"strconv"
)
// Reader contains the IO reader and its length, and custom ContentType and other headers.
type Reader struct {
ContentType string
ContentLength int64
Reader io.Reader
Headers map[string]string
}
// Render (Reader) writes data with custom ContentType and headers.
func (r Reader) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
r.writeHeaders(w, r.Headers)
_, err = io.Copy(w, r.Reader)
return
}
// WriteContentType (Reader) writes custom ContentType.
func (r Reader) WriteContentType(w http.ResponseWriter) {
writeContentType(w, []string{r.ContentType})
}
// writeHeaders writes custom Header.
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
header := w.Header()
for k, v := range headers {
if header.Get(k) == "" {
header.Set(k, v)
}
}
if r.ContentLength > 0 {
header.Set("Content-Length", strconv.FormatInt(r.ContentLength, 10))
}
}