2014-12-12 23:48:41 +00:00
|
|
|
package v2
|
|
|
|
|
|
|
|
import (
|
2017-03-01 18:24:08 +00:00
|
|
|
"fmt"
|
2015-01-31 19:43:06 +00:00
|
|
|
"net/http"
|
2014-12-12 23:48:41 +00:00
|
|
|
"net/url"
|
2017-03-01 18:24:08 +00:00
|
|
|
"reflect"
|
2014-12-12 23:48:41 +00:00
|
|
|
"testing"
|
2015-12-15 22:35:23 +00:00
|
|
|
|
|
|
|
"github.com/docker/distribution/reference"
|
2014-12-12 23:48:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type urlBuilderTestCase struct {
|
2015-01-31 19:43:06 +00:00
|
|
|
description string
|
|
|
|
expectedPath string
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr error
|
2015-01-31 19:43:06 +00:00
|
|
|
build func() (string, error)
|
2014-12-12 23:48:41 +00:00
|
|
|
}
|
|
|
|
|
2015-01-31 19:43:06 +00:00
|
|
|
func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
|
2017-01-14 01:06:03 +00:00
|
|
|
fooBarRef, _ := reference.WithName("foo/bar")
|
2015-01-31 19:43:06 +00:00
|
|
|
return []urlBuilderTestCase{
|
2014-12-12 23:48:41 +00:00
|
|
|
{
|
2015-01-31 19:43:06 +00:00
|
|
|
description: "test base url",
|
|
|
|
expectedPath: "/v2/",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2015-01-31 19:43:06 +00:00
|
|
|
build: urlBuilder.BuildBaseURL,
|
2014-12-12 23:48:41 +00:00
|
|
|
},
|
|
|
|
{
|
2015-01-31 19:43:06 +00:00
|
|
|
description: "test tags url",
|
|
|
|
expectedPath: "/v2/foo/bar/tags/list",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2014-12-12 23:48:41 +00:00
|
|
|
build: func() (string, error) {
|
2015-12-15 22:35:23 +00:00
|
|
|
return urlBuilder.BuildTagsURL(fooBarRef)
|
2014-12-12 23:48:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-03-01 18:24:08 +00:00
|
|
|
description: "test manifest url tagged ref",
|
2015-01-31 19:43:06 +00:00
|
|
|
expectedPath: "/v2/foo/bar/manifests/tag",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2014-12-12 23:48:41 +00:00
|
|
|
build: func() (string, error) {
|
2015-12-16 00:43:13 +00:00
|
|
|
ref, _ := reference.WithTag(fooBarRef, "tag")
|
|
|
|
return urlBuilder.BuildManifestURL(ref)
|
2014-12-12 23:48:41 +00:00
|
|
|
},
|
|
|
|
},
|
2017-03-01 18:24:08 +00:00
|
|
|
{
|
|
|
|
description: "test manifest url bare ref",
|
|
|
|
expectedPath: "",
|
|
|
|
expectedErr: fmt.Errorf("reference must have a tag or digest"),
|
|
|
|
build: func() (string, error) {
|
|
|
|
return urlBuilder.BuildManifestURL(fooBarRef)
|
|
|
|
},
|
|
|
|
},
|
2014-12-12 23:48:41 +00:00
|
|
|
{
|
2015-01-31 19:43:06 +00:00
|
|
|
description: "build blob url",
|
2015-12-16 01:18:13 +00:00
|
|
|
expectedPath: "/v2/foo/bar/blobs/sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2014-12-12 23:48:41 +00:00
|
|
|
build: func() (string, error) {
|
2015-12-16 00:43:13 +00:00
|
|
|
ref, _ := reference.WithDigest(fooBarRef, "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5")
|
|
|
|
return urlBuilder.BuildBlobURL(ref)
|
2014-12-12 23:48:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-01-31 19:43:06 +00:00
|
|
|
description: "build blob upload url",
|
|
|
|
expectedPath: "/v2/foo/bar/blobs/uploads/",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2014-12-12 23:48:41 +00:00
|
|
|
build: func() (string, error) {
|
2015-12-15 22:35:23 +00:00
|
|
|
return urlBuilder.BuildBlobUploadURL(fooBarRef)
|
2014-12-12 23:48:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-01-31 19:43:06 +00:00
|
|
|
description: "build blob upload url with digest and size",
|
2015-12-16 01:18:13 +00:00
|
|
|
expectedPath: "/v2/foo/bar/blobs/uploads/?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2014-12-12 23:48:41 +00:00
|
|
|
build: func() (string, error) {
|
2015-12-15 22:35:23 +00:00
|
|
|
return urlBuilder.BuildBlobUploadURL(fooBarRef, url.Values{
|
2014-12-12 23:48:41 +00:00
|
|
|
"size": []string{"10000"},
|
2015-12-16 01:18:13 +00:00
|
|
|
"digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"},
|
2014-12-12 23:48:41 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-01-31 19:43:06 +00:00
|
|
|
description: "build blob upload chunk url",
|
|
|
|
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2014-12-12 23:48:41 +00:00
|
|
|
build: func() (string, error) {
|
2015-12-15 22:35:23 +00:00
|
|
|
return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part")
|
2014-12-12 23:48:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-01-31 19:43:06 +00:00
|
|
|
description: "build blob upload chunk url with digest and size",
|
2015-12-16 01:18:13 +00:00
|
|
|
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr: nil,
|
2014-12-12 23:48:41 +00:00
|
|
|
build: func() (string, error) {
|
2015-12-15 22:35:23 +00:00
|
|
|
return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part", url.Values{
|
2014-12-12 23:48:41 +00:00
|
|
|
"size": []string{"10000"},
|
2015-12-16 01:18:13 +00:00
|
|
|
"digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"},
|
2014-12-12 23:48:41 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
},
|
2015-01-31 19:43:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestURLBuilder tests the various url building functions, ensuring they are
|
|
|
|
// returning the expected values.
|
|
|
|
func TestURLBuilder(t *testing.T) {
|
|
|
|
roots := []string{
|
|
|
|
"http://example.com",
|
|
|
|
"https://example.com",
|
|
|
|
"http://localhost:5000",
|
|
|
|
"https://localhost:5443",
|
|
|
|
}
|
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
doTest := func(relative bool) {
|
|
|
|
for _, root := range roots {
|
|
|
|
urlBuilder, err := NewURLBuilderFromString(root, relative)
|
2015-01-31 19:43:06 +00:00
|
|
|
if err != nil {
|
2016-02-23 01:49:23 +00:00
|
|
|
t.Fatalf("unexpected error creating urlbuilder: %v", err)
|
2015-01-31 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
for _, testCase := range makeURLBuilderTestCases(urlBuilder) {
|
|
|
|
url, err := testCase.build()
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr := testCase.expectedErr
|
|
|
|
if !reflect.DeepEqual(expectedErr, err) {
|
|
|
|
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
|
|
|
}
|
|
|
|
if expectedErr != nil {
|
|
|
|
continue
|
2016-02-23 01:49:23 +00:00
|
|
|
}
|
2017-03-01 18:24:08 +00:00
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
expectedURL := testCase.expectedPath
|
|
|
|
if !relative {
|
|
|
|
expectedURL = root + expectedURL
|
|
|
|
}
|
2015-01-31 19:43:06 +00:00
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
if url != expectedURL {
|
|
|
|
t.Fatalf("%s: %q != %q", testCase.description, url, expectedURL)
|
|
|
|
}
|
2015-01-31 19:43:06 +00:00
|
|
|
}
|
2014-12-12 23:48:41 +00:00
|
|
|
}
|
|
|
|
}
|
2016-02-23 01:49:23 +00:00
|
|
|
doTest(true)
|
|
|
|
doTest(false)
|
2015-01-31 19:43:06 +00:00
|
|
|
}
|
2014-12-12 23:48:41 +00:00
|
|
|
|
2015-02-24 22:59:01 +00:00
|
|
|
func TestURLBuilderWithPrefix(t *testing.T) {
|
|
|
|
roots := []string{
|
|
|
|
"http://example.com/prefix/",
|
|
|
|
"https://example.com/prefix/",
|
|
|
|
"http://localhost:5000/prefix/",
|
|
|
|
"https://localhost:5443/prefix/",
|
|
|
|
}
|
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
doTest := func(relative bool) {
|
|
|
|
for _, root := range roots {
|
|
|
|
urlBuilder, err := NewURLBuilderFromString(root, relative)
|
2015-02-24 22:59:01 +00:00
|
|
|
if err != nil {
|
2016-02-23 01:49:23 +00:00
|
|
|
t.Fatalf("unexpected error creating urlbuilder: %v", err)
|
2015-02-24 22:59:01 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
for _, testCase := range makeURLBuilderTestCases(urlBuilder) {
|
|
|
|
url, err := testCase.build()
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr := testCase.expectedErr
|
|
|
|
if !reflect.DeepEqual(expectedErr, err) {
|
|
|
|
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
|
|
|
}
|
|
|
|
if expectedErr != nil {
|
|
|
|
continue
|
2016-02-23 01:49:23 +00:00
|
|
|
}
|
2015-02-24 22:59:01 +00:00
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
expectedURL := testCase.expectedPath
|
|
|
|
if !relative {
|
|
|
|
expectedURL = root[0:len(root)-1] + expectedURL
|
|
|
|
}
|
|
|
|
if url != expectedURL {
|
|
|
|
t.Fatalf("%s: %q != %q", testCase.description, url, expectedURL)
|
|
|
|
}
|
2015-02-24 22:59:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-23 01:49:23 +00:00
|
|
|
doTest(true)
|
|
|
|
doTest(false)
|
2015-02-24 22:59:01 +00:00
|
|
|
}
|
|
|
|
|
2015-01-31 19:43:06 +00:00
|
|
|
type builderFromRequestTestCase struct {
|
|
|
|
request *http.Request
|
|
|
|
base string
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuilderFromRequest(t *testing.T) {
|
|
|
|
u, err := url.Parse("http://example.com")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
testRequests := []struct {
|
2016-10-13 15:59:49 +00:00
|
|
|
name string
|
2015-09-18 18:03:15 +00:00
|
|
|
request *http.Request
|
|
|
|
base string
|
|
|
|
configHost url.URL
|
2015-01-31 19:43:06 +00:00
|
|
|
}{
|
|
|
|
{
|
2016-10-13 15:59:49 +00:00
|
|
|
name: "no forwarded header",
|
2015-01-31 19:43:06 +00:00
|
|
|
request: &http.Request{URL: u, Host: u.Host},
|
|
|
|
base: "http://example.com",
|
|
|
|
},
|
2015-10-15 08:56:28 +00:00
|
|
|
{
|
2016-10-13 15:59:49 +00:00
|
|
|
name: "https protocol forwarded with a non-standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Proto": []string{"https"},
|
|
|
|
}},
|
|
|
|
base: "http://example.com",
|
2015-10-15 08:56:28 +00:00
|
|
|
},
|
2015-01-31 19:43:06 +00:00
|
|
|
{
|
2016-10-13 15:59:49 +00:00
|
|
|
name: "forwarded protocol is the same",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Proto": []string{"https"},
|
|
|
|
}},
|
|
|
|
base: "https://example.com",
|
2015-01-31 19:43:06 +00:00
|
|
|
},
|
2015-04-24 21:04:48 +00:00
|
|
|
{
|
2016-10-13 15:59:49 +00:00
|
|
|
name: "forwarded host with a non-standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Host": []string{"first.example.com"},
|
|
|
|
}},
|
|
|
|
base: "http://first.example.com",
|
2015-04-24 21:04:48 +00:00
|
|
|
},
|
|
|
|
{
|
2016-10-13 15:59:49 +00:00
|
|
|
name: "forwarded multiple hosts a with non-standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Host": []string{"first.example.com, proxy1.example.com"},
|
|
|
|
}},
|
|
|
|
base: "http://first.example.com",
|
2015-04-24 21:04:48 +00:00
|
|
|
},
|
2015-09-18 18:03:15 +00:00
|
|
|
{
|
2016-10-13 15:59:49 +00:00
|
|
|
name: "host configured in config file takes priority",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Host": []string{"first.example.com, proxy1.example.com"},
|
|
|
|
}},
|
|
|
|
base: "https://third.example.com:5000",
|
2015-09-18 18:03:15 +00:00
|
|
|
configHost: url.URL{
|
|
|
|
Scheme: "https",
|
|
|
|
Host: "third.example.com:5000",
|
|
|
|
},
|
|
|
|
},
|
2016-10-13 15:59:49 +00:00
|
|
|
{
|
|
|
|
name: "forwarded host and port with just one non-standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Host": []string{"first.example.com:443"},
|
|
|
|
}},
|
|
|
|
base: "http://first.example.com:443",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "forwarded port with a non-standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Port": []string{"5000"},
|
|
|
|
}},
|
|
|
|
base: "http://example.com:5000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "forwarded multiple ports with a non-standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Port": []string{"443 , 5001"},
|
|
|
|
}},
|
|
|
|
base: "http://example.com:443",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "several non-standard headers",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Proto": []string{"https"},
|
|
|
|
"X-Forwarded-Host": []string{" first.example.com "},
|
|
|
|
"X-Forwarded-Port": []string{" 12345 \t"},
|
|
|
|
}},
|
|
|
|
base: "http://first.example.com:12345",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "forwarded host with port supplied takes priority",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Host": []string{"first.example.com:5000"},
|
|
|
|
"X-Forwarded-Port": []string{"80"},
|
|
|
|
}},
|
|
|
|
base: "http://first.example.com:5000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "malformed forwarded port",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Host": []string{"first.example.com"},
|
|
|
|
"X-Forwarded-Port": []string{"abcd"},
|
|
|
|
}},
|
|
|
|
base: "http://first.example.com",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "forwarded protocol and addr using standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`proto=https;for="192.168.22.30:80"`},
|
|
|
|
}},
|
|
|
|
base: "https://192.168.22.30:80",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "forwarded addr takes priority over host",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`host=reg.example.com;for="192.168.22.30:5000"`},
|
|
|
|
}},
|
|
|
|
base: "http://192.168.22.30:5000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "forwarded host and protocol using standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`host=reg.example.com;proto=https`},
|
|
|
|
}},
|
|
|
|
base: "https://reg.example.com",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "process just the first standard forwarded header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`host="reg.example.com:88";proto=http`, `host=reg.example.com;proto=https`},
|
|
|
|
}},
|
|
|
|
base: "http://reg.example.com:88",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "process just the first list element of standard header",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="reg.example.com:443";proto=https, for="reg.example.com:80";proto=http`},
|
|
|
|
}},
|
|
|
|
base: "https://reg.example.com:443",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address override port",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="2607:f0d0:1002:51::4"`},
|
|
|
|
"X-Forwarded-Port": []string{"5001"},
|
|
|
|
}},
|
|
|
|
base: "http://[2607:f0d0:1002:51::4]:5001",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address with port",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="[2607:f0d0:1002:51::4]:4000"`},
|
|
|
|
"X-Forwarded-Port": []string{"5001"},
|
|
|
|
}},
|
|
|
|
base: "http://[2607:f0d0:1002:51::4]:4000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 long address override port",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="2607:f0d0:1002:0051:0000:0000:0000:0004"`},
|
|
|
|
"X-Forwarded-Port": []string{"5001"},
|
|
|
|
}},
|
|
|
|
base: "http://[2607:f0d0:1002:0051:0000:0000:0000:0004]:5001",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 long address enclosed in brackets - be benevolent",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="[2607:f0d0:1002:0051:0000:0000:0000:0004]"`},
|
|
|
|
"X-Forwarded-Port": []string{"5001"},
|
|
|
|
}},
|
|
|
|
base: "http://[2607:f0d0:1002:0051:0000:0000:0000:0004]:5001",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 long address with port",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="[2607:f0d0:1002:0051:0000:0000:0000:0004]:4321"`},
|
|
|
|
"X-Forwarded-Port": []string{"5001"},
|
|
|
|
}},
|
|
|
|
base: "http://[2607:f0d0:1002:0051:0000:0000:0000:0004]:4321",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address with zone ID",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="fe80::bd0f:a8bc:6480:238b%11"`},
|
|
|
|
"X-Forwarded-Port": []string{"5001"},
|
|
|
|
}},
|
|
|
|
base: "http://[fe80::bd0f:a8bc:6480:238b%2511]:5001",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address with zone ID and port",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="[fe80::bd0f:a8bc:6480:238b%eth0]:12345"`},
|
|
|
|
"X-Forwarded-Port": []string{"5001"},
|
|
|
|
}},
|
|
|
|
base: "http://[fe80::bd0f:a8bc:6480:238b%25eth0]:12345",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address without port",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"Forwarded": []string{`for="::FFFF:129.144.52.38"`},
|
|
|
|
}},
|
|
|
|
base: "http://[::FFFF:129.144.52.38]",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "non-standard and standard forward headers",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Proto": []string{`https`},
|
|
|
|
"X-Forwarded-Host": []string{`first.example.com`},
|
|
|
|
"X-Forwarded-Port": []string{``},
|
|
|
|
"Forwarded": []string{`host=first.example.com; proto=https`},
|
|
|
|
}},
|
|
|
|
base: "https://first.example.com",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "non-standard headers take precedence over standard one",
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: http.Header{
|
|
|
|
"X-Forwarded-Proto": []string{`http`},
|
|
|
|
"Forwarded": []string{`host=second.example.com; proto=https`},
|
|
|
|
"X-Forwarded-Host": []string{`first.example.com`},
|
|
|
|
"X-Forwarded-Port": []string{`4000`},
|
|
|
|
}},
|
|
|
|
base: "http://first.example.com:4000",
|
|
|
|
},
|
2015-01-31 19:43:06 +00:00
|
|
|
}
|
2016-10-13 15:59:49 +00:00
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
doTest := func(relative bool) {
|
|
|
|
for _, tr := range testRequests {
|
|
|
|
var builder *URLBuilder
|
|
|
|
if tr.configHost.Scheme != "" && tr.configHost.Host != "" {
|
|
|
|
builder = NewURLBuilder(&tr.configHost, relative)
|
|
|
|
} else {
|
|
|
|
builder = NewURLBuilderFromRequest(tr.request, relative)
|
2015-01-31 19:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
for _, testCase := range makeURLBuilderTestCases(builder) {
|
|
|
|
buildURL, err := testCase.build()
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr := testCase.expectedErr
|
|
|
|
if !reflect.DeepEqual(expectedErr, err) {
|
|
|
|
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
|
|
|
}
|
|
|
|
if expectedErr != nil {
|
|
|
|
continue
|
2015-10-15 08:56:28 +00:00
|
|
|
}
|
2015-01-31 19:43:06 +00:00
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
var expectedURL string
|
|
|
|
proto, ok := tr.request.Header["X-Forwarded-Proto"]
|
|
|
|
if !ok {
|
|
|
|
expectedURL = testCase.expectedPath
|
|
|
|
if !relative {
|
|
|
|
expectedURL = tr.base + expectedURL
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
urlBase, err := url.Parse(tr.base)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
urlBase.Scheme = proto[0]
|
|
|
|
expectedURL = testCase.expectedPath
|
|
|
|
if !relative {
|
|
|
|
expectedURL = urlBase.String() + expectedURL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if buildURL != expectedURL {
|
2016-10-13 15:59:49 +00:00
|
|
|
t.Errorf("[relative=%t, request=%q, case=%q]: %q != %q", relative, tr.name, testCase.description, buildURL, expectedURL)
|
2016-02-23 01:49:23 +00:00
|
|
|
}
|
2015-01-31 19:43:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-13 15:59:49 +00:00
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
doTest(true)
|
|
|
|
doTest(false)
|
2014-12-12 23:48:41 +00:00
|
|
|
}
|
2015-02-24 22:59:01 +00:00
|
|
|
|
|
|
|
func TestBuilderFromRequestWithPrefix(t *testing.T) {
|
|
|
|
u, err := url.Parse("http://example.com/prefix/v2/")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
forwardedProtoHeader := make(http.Header, 1)
|
|
|
|
forwardedProtoHeader.Set("X-Forwarded-Proto", "https")
|
|
|
|
|
|
|
|
testRequests := []struct {
|
2015-09-18 18:03:15 +00:00
|
|
|
request *http.Request
|
|
|
|
base string
|
|
|
|
configHost url.URL
|
2015-02-24 22:59:01 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
request: &http.Request{URL: u, Host: u.Host},
|
|
|
|
base: "http://example.com/prefix/",
|
|
|
|
},
|
2015-10-15 08:56:28 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
|
|
|
|
base: "http://example.com/prefix/",
|
|
|
|
},
|
2015-02-24 22:59:01 +00:00
|
|
|
{
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
|
|
|
|
base: "https://example.com/prefix/",
|
|
|
|
},
|
2015-09-18 18:03:15 +00:00
|
|
|
{
|
|
|
|
request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
|
|
|
|
base: "https://subdomain.example.com/prefix/",
|
|
|
|
configHost: url.URL{
|
|
|
|
Scheme: "https",
|
2015-10-25 17:01:45 +00:00
|
|
|
Host: "subdomain.example.com",
|
|
|
|
Path: "/prefix/",
|
2015-09-18 18:03:15 +00:00
|
|
|
},
|
|
|
|
},
|
2015-02-24 22:59:01 +00:00
|
|
|
}
|
|
|
|
|
2016-02-23 01:49:23 +00:00
|
|
|
var relative bool
|
2015-02-24 22:59:01 +00:00
|
|
|
for _, tr := range testRequests {
|
2015-09-18 18:03:15 +00:00
|
|
|
var builder *URLBuilder
|
|
|
|
if tr.configHost.Scheme != "" && tr.configHost.Host != "" {
|
2016-02-23 01:49:23 +00:00
|
|
|
builder = NewURLBuilder(&tr.configHost, false)
|
2015-09-18 18:03:15 +00:00
|
|
|
} else {
|
2016-02-23 01:49:23 +00:00
|
|
|
builder = NewURLBuilderFromRequest(tr.request, false)
|
2015-09-18 18:03:15 +00:00
|
|
|
}
|
2015-02-24 22:59:01 +00:00
|
|
|
|
|
|
|
for _, testCase := range makeURLBuilderTestCases(builder) {
|
2015-10-15 08:56:28 +00:00
|
|
|
buildURL, err := testCase.build()
|
2017-03-01 18:24:08 +00:00
|
|
|
expectedErr := testCase.expectedErr
|
|
|
|
if !reflect.DeepEqual(expectedErr, err) {
|
|
|
|
t.Fatalf("%s: Expecting %v but got error %v", testCase.description, expectedErr, err)
|
|
|
|
}
|
|
|
|
if expectedErr != nil {
|
|
|
|
continue
|
2015-02-24 22:59:01 +00:00
|
|
|
}
|
2016-02-23 01:49:23 +00:00
|
|
|
|
2015-10-15 08:56:28 +00:00
|
|
|
var expectedURL string
|
|
|
|
proto, ok := tr.request.Header["X-Forwarded-Proto"]
|
|
|
|
if !ok {
|
2016-02-23 01:49:23 +00:00
|
|
|
expectedURL = testCase.expectedPath
|
|
|
|
if !relative {
|
|
|
|
expectedURL = tr.base[0:len(tr.base)-1] + expectedURL
|
|
|
|
}
|
2015-10-15 08:56:28 +00:00
|
|
|
} else {
|
|
|
|
urlBase, err := url.Parse(tr.base)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
urlBase.Scheme = proto[0]
|
2016-02-23 01:49:23 +00:00
|
|
|
expectedURL = testCase.expectedPath
|
|
|
|
if !relative {
|
|
|
|
expectedURL = urlBase.String()[0:len(urlBase.String())-1] + expectedURL
|
|
|
|
}
|
|
|
|
|
2015-10-15 08:56:28 +00:00
|
|
|
}
|
2015-02-24 22:59:01 +00:00
|
|
|
|
2015-10-15 08:56:28 +00:00
|
|
|
if buildURL != expectedURL {
|
|
|
|
t.Fatalf("%s: %q != %q", testCase.description, buildURL, expectedURL)
|
2015-02-24 22:59:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-13 15:59:49 +00:00
|
|
|
|
|
|
|
func TestIsIPv6Address(t *testing.T) {
|
|
|
|
for _, tc := range []struct {
|
|
|
|
name string
|
|
|
|
address string
|
|
|
|
isIPv6 bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "IPv6 short address",
|
|
|
|
address: `2607:f0d0:1002:51::4`,
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 short address enclosed in brackets",
|
|
|
|
address: "[2607:f0d0:1002:51::4]",
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address",
|
|
|
|
address: `2607:f0d0:1002:0051:0000:0000:0000:0004`,
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address with numeric zone ID",
|
|
|
|
address: `fe80::bd0f:a8bc:6480:238b%11`,
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address with device name as zone ID",
|
|
|
|
address: `fe80::bd0f:a8bc:6480:238b%eth0`,
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 address with device name as zone ID enclosed in brackets",
|
|
|
|
address: `[fe80::bd0f:a8bc:6480:238b%eth0]`,
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv4-mapped address",
|
|
|
|
address: "::FFFF:129.144.52.38",
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "localhost",
|
|
|
|
address: "::1",
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "localhost",
|
|
|
|
address: "::1",
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "long localhost address",
|
|
|
|
address: "0:0:0:0:0:0:0:1",
|
|
|
|
isIPv6: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv6 long address with port",
|
|
|
|
address: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:4321",
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "too many groups",
|
|
|
|
address: "2607:f0d0:1002:0051:0000:0000:0000:0004:4321",
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "square brackets don't make an IPv6 address",
|
|
|
|
address: "[2607:f0d0]",
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "require two consecutive colons in localhost",
|
|
|
|
address: ":1",
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "more then 4 hexadecimal digits",
|
|
|
|
address: "2607:f0d0b:1002:0051:0000:0000:0000:0004",
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "too short address",
|
|
|
|
address: `2607:f0d0:1002:0000:0000:0000:0004`,
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "IPv4 address",
|
|
|
|
address: `192.168.100.1`,
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "unclosed bracket",
|
|
|
|
address: `[2607:f0d0:1002:0051:0000:0000:0000:0004`,
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "trailing bracket",
|
|
|
|
address: `2607:f0d0:1002:0051:0000:0000:0000:0004]`,
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "domain name",
|
|
|
|
address: `localhost`,
|
|
|
|
isIPv6: false,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
isIPv6 := isIPv6Address(tc.address)
|
|
|
|
if isIPv6 && !tc.isIPv6 {
|
|
|
|
t.Errorf("[%s] address %q falsely detected as IPv6 address", tc.name, tc.address)
|
|
|
|
} else if !isIPv6 && tc.isIPv6 {
|
|
|
|
t.Errorf("[%s] address %q not recognized as IPv6", tc.name, tc.address)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|