Honor X-Forwarded-Port and Forwarded headers
Prefer non-standard headers like X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Port over the standard Forwarded header to maintain backwards compatibility. If a port is not specified neither in Host nor in forwarded headers but it is specified just with X-Forwarded-Port, use its value in base urls for redirects. Forwarded header is defined in rfc7239. X-Forwarded-Port is a non-standard header. Here's a description copied from "HTTP Headers and Elastic Load Balancing" of AWS ELB docs: > The X-Forwarded-Port request header helps you identify the port that > an HTTP or HTTPS load balancer uses to connect to the client. Signed-off-by: Michal Minář <miminar@redhat.com>
This commit is contained in:
parent
d0cdc4802b
commit
1b43e1e30d
4 changed files with 698 additions and 24 deletions
161
registry/api/v2/headerparser_test.go
Normal file
161
registry/api/v2/headerparser_test.go
Normal file
|
@ -0,0 +1,161 @@
|
|||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseForwardedHeader(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
raw string
|
||||
expected map[string]string
|
||||
expectedRest string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
raw: "",
|
||||
},
|
||||
{
|
||||
name: "one pair",
|
||||
raw: " key = value ",
|
||||
expected: map[string]string{"key": "value"},
|
||||
},
|
||||
{
|
||||
name: "two pairs",
|
||||
raw: " key1 = value1; key2=value2",
|
||||
expected: map[string]string{"key1": "value1", "key2": "value2"},
|
||||
},
|
||||
{
|
||||
name: "uppercase parameter",
|
||||
raw: "KeY=VaL",
|
||||
expected: map[string]string{"key": "VaL"},
|
||||
},
|
||||
{
|
||||
name: "missing key=value pair - be tolerant",
|
||||
raw: "key=val;",
|
||||
expected: map[string]string{"key": "val"},
|
||||
},
|
||||
{
|
||||
name: "quoted values",
|
||||
raw: `key="val";param = "[[ $((1 + 1)) == 3 ]] && echo panic!;" ; p=" abcd "`,
|
||||
expected: map[string]string{"key": "val", "param": "[[ $((1 + 1)) == 3 ]] && echo panic!;", "p": " abcd "},
|
||||
},
|
||||
{
|
||||
name: "empty quoted value",
|
||||
raw: `key=""`,
|
||||
expected: map[string]string{"key": ""},
|
||||
},
|
||||
{
|
||||
name: "quoted double quotes",
|
||||
raw: `key="\"value\""`,
|
||||
expected: map[string]string{"key": `"value"`},
|
||||
},
|
||||
{
|
||||
name: "quoted backslash",
|
||||
raw: `key="\"\\\""`,
|
||||
expected: map[string]string{"key": `"\"`},
|
||||
},
|
||||
{
|
||||
name: "ignore subsequent elements",
|
||||
raw: "key=a, param= b",
|
||||
expected: map[string]string{"key": "a"},
|
||||
expectedRest: " param= b",
|
||||
},
|
||||
{
|
||||
name: "empty element - be tolerant",
|
||||
raw: " , key=val",
|
||||
expectedRest: " key=val",
|
||||
},
|
||||
{
|
||||
name: "obscure key",
|
||||
raw: `ob₷C&r€ = value`,
|
||||
expected: map[string]string{`ob₷c&r€`: "value"},
|
||||
},
|
||||
{
|
||||
name: "duplicate parameter",
|
||||
raw: "key=a; p=b; key=c",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "empty parameter",
|
||||
raw: "=value",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "empty value",
|
||||
raw: "key= ",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "empty value before a new element ",
|
||||
raw: "key=,",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "empty value before a new pair",
|
||||
raw: "key=;",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "just parameter",
|
||||
raw: "key",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "missing key-value",
|
||||
raw: "a=b;;",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "unclosed quoted value",
|
||||
raw: `key="value`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "escaped terminating dquote",
|
||||
raw: `key="value\"`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "just a quoted value",
|
||||
raw: `"key=val"`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "quoted key",
|
||||
raw: `"key"=val`,
|
||||
expectedError: true,
|
||||
},
|
||||
} {
|
||||
parsed, rest, err := parseForwardedHeader(tc.raw)
|
||||
if err != nil && !tc.expectedError {
|
||||
t.Errorf("[%s] got unexpected error: %v", tc.name, err)
|
||||
}
|
||||
if err == nil && tc.expectedError {
|
||||
t.Errorf("[%s] got unexpected non-error", tc.name)
|
||||
}
|
||||
if err != nil || tc.expectedError {
|
||||
continue
|
||||
}
|
||||
for key, value := range tc.expected {
|
||||
v, exists := parsed[key]
|
||||
if !exists {
|
||||
t.Errorf("[%s] missing expected parameter %q", tc.name, key)
|
||||
continue
|
||||
}
|
||||
if v != value {
|
||||
t.Errorf("[%s] got unexpected value for parameter %q: %q != %q", tc.name, key, v, value)
|
||||
}
|
||||
}
|
||||
for key, value := range parsed {
|
||||
if _, exists := tc.expected[key]; !exists {
|
||||
t.Errorf("[%s] got unexpected key/value pair: %q=%q", tc.name, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
if rest != tc.expectedRest {
|
||||
t.Errorf("[%s] got unexpected unparsed string: %q != %q", tc.name, rest, tc.expectedRest)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue