Skip to content

Bug? wrapContentDecompresser uses case-sensitive lookup for content-coding values, violating RFC 9110 #1111

@hiraqdev

Description

@hiraqdev

wrapContentDecompresser in response.go performs an exact (case-sensitive) map key lookup using the Content-Encoding header value (the content-coding token). However, RFC 9110 Section 8.4.1 specifies that content-coding values are case-insensitive:

Content-coding values are used to indicate an encoding transformation that has been
or can be applied to a representation. Content codings are case-insensitive.

This means a server returning Content-Encoding: BR, Br, or bR is valid per the spec, but resty returns ErrContentDecompresserNotFound because the decompresser was registered under lowercase "br".

Where

response.go lines 310-335 (v3branch):

func (r *Response) wrapContentDecompresser() error {
    ce := r.Header().Get(hdrContentEncodingKey) // line 311: gets the content-coding
value (e.g. "BR")
    if isStringEmpty(ce) {
        return nil
    }

    if decFunc, f := r.Request.client.ContentDecompressers()[ce]; f { // line 316:exact map[string] lookup
        // ...
    } else {
        return ErrContentDecompresserNotFound // line 331: reached when casing differs
    }

    return nil
}

Header.Get() returns the raw header value as-is. That value is then used as an exact key into the ContentDecompressers() map. Since AddContentDecompresser stores the key as provided (e.g. "br"), a response with Content-Encoding: BR won't match.

Reproduce

package main

import (
      "fmt"
      "io"
      "net/http"
      "net/http/httptest"

      "resty.dev/v3"
)

func main() {
      server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) {
              w.Header().Set("Content-Encoding", "BR") // uppercase — valid per RFC 911
              w.WriteHeader(200)
              w.Write([]byte("data"))
      }))
      defer server.Close()

      client := resty.New()
      client.AddContentDecompresser("br", func(body io.ReadCloser) (io.ReadCloser, erro
              return body, nil // dummy decompresser
      })

      _, err := client.R().Get(server.URL)
      fmt.Println(err) // "resty: content decoder not found"
}

Output:

resty: content decoder not found

Changing "BR" to "br" on the server side makes the error disappear, confirming the lookup is case-sensitive.

Example:

  ┌──────┬──────────────────┬─────────────────────────────────────────┐         
  │ Case │ Content-Encoding │                 Result                  │       
  ├──────┼──────────────────┼─────────────────────────────────────────┤
  │ 1    │ br (lowercase)   │ OK - no error                           │         
  ├──────┼──────────────────┼─────────────────────────────────────────┤         
  │ 2    │ BR (uppercase)   │ ERROR: resty: content decoder not found │         
  ├──────┼──────────────────┼─────────────────────────────────────────┤  
  │ 3    │ Br (mixed)       │ ERROR: resty: content decoder not found │
  └──────┴──────────────────┴─────────────────────────────────────────┘

Metadata

Metadata

Assignees

Labels

bugv3For resty v3

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions