Description
When header tags are added to a struct member for gin to use in BindHeader
, they will be case-sensitive in the code. The Bind
function for headers in turn relies directly on the http.Request.Header
map instead of making use of the Header.Get
function, which handles MIME decoding and ignores case when retrieving values. As a result, let's say you've got load balancers or some other infrastructure that rewrites your headers along the way, or the calling client uses all lower-case when your tags have specified the correct MIME standard case (eg. Content-Type
as opposed to content-type
), the headers will not marshal.
Tracing the gin code, the bind header function access the Header map[string][]string
directly.
The following test illustrates the issue:
How to reproduce
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/gin-gonic/gin"
)
type MyRequest struct {
MyHeader string `header:"Content-Type"`
CustHeader string `header:"X-Custom-Header"`
}
func main() {
var headers MyRequest
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = &http.Request{
Header: make(http.Header),
}
c.Request.Header["content-TYPE"] = []string{"this/will/not/bind"}
c.Request.Header["x-custom-header"] = []string{"this/will/not/work"}
err := c.BindHeader(&headers)
if err != nil {
fmt.Printf("BindHeaders failed: %v\n", err)
} else {
fmt.Printf("Test 1: Header cases don't match:\n")
fmt.Printf("MyRequest struct after binding: %+v\n\n", headers) // Will print nothing because the struct wasn't populated
}
headers = MyRequest{}
c.Request.Header["Content-Type"] = []string{"this/will/bind"}
c.Request.Header.Add("X-cuSTOM-HEADer", "this/works")
err = c.BindHeader(&headers)
if err != nil {
fmt.Printf("BindHeaders failed: %v\n", err)
} else {
fmt.Printf("Test 2: Header cases match or normalized with Header.Add:\n")
fmt.Printf("MyRequest struct after binding: %+v\n", headers) // Will be populated
}
}
Expectations
The above code illustrates expectations. If fields are stated as Content-Type
but a curl command is issued against a server, such as curl -H "content-type: text/plain" http://localhost:8888/hello/world
and the code depends on that struct element, the code will incorrectly fail.
Environment
- go1.21
- gin-gonic v1.9.1