github.com/npaton/distribution@v2.3.1-rc.0+incompatible/registry/api/v2/urls_test.go (about)

     1  package v2
     2  
     3  import (
     4  	"net/http"
     5  	"net/url"
     6  	"testing"
     7  
     8  	"github.com/docker/distribution/reference"
     9  )
    10  
    11  type urlBuilderTestCase struct {
    12  	description  string
    13  	expectedPath string
    14  	build        func() (string, error)
    15  }
    16  
    17  func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
    18  	fooBarRef, _ := reference.ParseNamed("foo/bar")
    19  	return []urlBuilderTestCase{
    20  		{
    21  			description:  "test base url",
    22  			expectedPath: "/v2/",
    23  			build:        urlBuilder.BuildBaseURL,
    24  		},
    25  		{
    26  			description:  "test tags url",
    27  			expectedPath: "/v2/foo/bar/tags/list",
    28  			build: func() (string, error) {
    29  				return urlBuilder.BuildTagsURL(fooBarRef)
    30  			},
    31  		},
    32  		{
    33  			description:  "test manifest url",
    34  			expectedPath: "/v2/foo/bar/manifests/tag",
    35  			build: func() (string, error) {
    36  				ref, _ := reference.WithTag(fooBarRef, "tag")
    37  				return urlBuilder.BuildManifestURL(ref)
    38  			},
    39  		},
    40  		{
    41  			description:  "build blob url",
    42  			expectedPath: "/v2/foo/bar/blobs/sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
    43  			build: func() (string, error) {
    44  				ref, _ := reference.WithDigest(fooBarRef, "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5")
    45  				return urlBuilder.BuildBlobURL(ref)
    46  			},
    47  		},
    48  		{
    49  			description:  "build blob upload url",
    50  			expectedPath: "/v2/foo/bar/blobs/uploads/",
    51  			build: func() (string, error) {
    52  				return urlBuilder.BuildBlobUploadURL(fooBarRef)
    53  			},
    54  		},
    55  		{
    56  			description:  "build blob upload url with digest and size",
    57  			expectedPath: "/v2/foo/bar/blobs/uploads/?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
    58  			build: func() (string, error) {
    59  				return urlBuilder.BuildBlobUploadURL(fooBarRef, url.Values{
    60  					"size":   []string{"10000"},
    61  					"digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"},
    62  				})
    63  			},
    64  		},
    65  		{
    66  			description:  "build blob upload chunk url",
    67  			expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part",
    68  			build: func() (string, error) {
    69  				return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part")
    70  			},
    71  		},
    72  		{
    73  			description:  "build blob upload chunk url with digest and size",
    74  			expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
    75  			build: func() (string, error) {
    76  				return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part", url.Values{
    77  					"size":   []string{"10000"},
    78  					"digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"},
    79  				})
    80  			},
    81  		},
    82  	}
    83  }
    84  
    85  // TestURLBuilder tests the various url building functions, ensuring they are
    86  // returning the expected values.
    87  func TestURLBuilder(t *testing.T) {
    88  	roots := []string{
    89  		"http://example.com",
    90  		"https://example.com",
    91  		"http://localhost:5000",
    92  		"https://localhost:5443",
    93  	}
    94  
    95  	for _, root := range roots {
    96  		urlBuilder, err := NewURLBuilderFromString(root)
    97  		if err != nil {
    98  			t.Fatalf("unexpected error creating urlbuilder: %v", err)
    99  		}
   100  
   101  		for _, testCase := range makeURLBuilderTestCases(urlBuilder) {
   102  			url, err := testCase.build()
   103  			if err != nil {
   104  				t.Fatalf("%s: error building url: %v", testCase.description, err)
   105  			}
   106  
   107  			expectedURL := root + testCase.expectedPath
   108  
   109  			if url != expectedURL {
   110  				t.Fatalf("%s: %q != %q", testCase.description, url, expectedURL)
   111  			}
   112  		}
   113  	}
   114  }
   115  
   116  func TestURLBuilderWithPrefix(t *testing.T) {
   117  	roots := []string{
   118  		"http://example.com/prefix/",
   119  		"https://example.com/prefix/",
   120  		"http://localhost:5000/prefix/",
   121  		"https://localhost:5443/prefix/",
   122  	}
   123  
   124  	for _, root := range roots {
   125  		urlBuilder, err := NewURLBuilderFromString(root)
   126  		if err != nil {
   127  			t.Fatalf("unexpected error creating urlbuilder: %v", err)
   128  		}
   129  
   130  		for _, testCase := range makeURLBuilderTestCases(urlBuilder) {
   131  			url, err := testCase.build()
   132  			if err != nil {
   133  				t.Fatalf("%s: error building url: %v", testCase.description, err)
   134  			}
   135  
   136  			expectedURL := root[0:len(root)-1] + testCase.expectedPath
   137  
   138  			if url != expectedURL {
   139  				t.Fatalf("%s: %q != %q", testCase.description, url, expectedURL)
   140  			}
   141  		}
   142  	}
   143  }
   144  
   145  type builderFromRequestTestCase struct {
   146  	request *http.Request
   147  	base    string
   148  }
   149  
   150  func TestBuilderFromRequest(t *testing.T) {
   151  	u, err := url.Parse("http://example.com")
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	forwardedProtoHeader := make(http.Header, 1)
   157  	forwardedProtoHeader.Set("X-Forwarded-Proto", "https")
   158  
   159  	forwardedHostHeader1 := make(http.Header, 1)
   160  	forwardedHostHeader1.Set("X-Forwarded-Host", "first.example.com")
   161  
   162  	forwardedHostHeader2 := make(http.Header, 1)
   163  	forwardedHostHeader2.Set("X-Forwarded-Host", "first.example.com, proxy1.example.com")
   164  
   165  	testRequests := []struct {
   166  		request    *http.Request
   167  		base       string
   168  		configHost url.URL
   169  	}{
   170  		{
   171  			request: &http.Request{URL: u, Host: u.Host},
   172  			base:    "http://example.com",
   173  		},
   174  
   175  		{
   176  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
   177  			base:    "http://example.com",
   178  		},
   179  		{
   180  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
   181  			base:    "https://example.com",
   182  		},
   183  		{
   184  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedHostHeader1},
   185  			base:    "http://first.example.com",
   186  		},
   187  		{
   188  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedHostHeader2},
   189  			base:    "http://first.example.com",
   190  		},
   191  		{
   192  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedHostHeader2},
   193  			base:    "https://third.example.com:5000",
   194  			configHost: url.URL{
   195  				Scheme: "https",
   196  				Host:   "third.example.com:5000",
   197  			},
   198  		},
   199  	}
   200  
   201  	for _, tr := range testRequests {
   202  		var builder *URLBuilder
   203  		if tr.configHost.Scheme != "" && tr.configHost.Host != "" {
   204  			builder = NewURLBuilder(&tr.configHost)
   205  		} else {
   206  			builder = NewURLBuilderFromRequest(tr.request)
   207  		}
   208  
   209  		for _, testCase := range makeURLBuilderTestCases(builder) {
   210  			buildURL, err := testCase.build()
   211  			if err != nil {
   212  				t.Fatalf("%s: error building url: %v", testCase.description, err)
   213  			}
   214  
   215  			var expectedURL string
   216  			proto, ok := tr.request.Header["X-Forwarded-Proto"]
   217  			if !ok {
   218  				expectedURL = tr.base + testCase.expectedPath
   219  			} else {
   220  				urlBase, err := url.Parse(tr.base)
   221  				if err != nil {
   222  					t.Fatal(err)
   223  				}
   224  				urlBase.Scheme = proto[0]
   225  				expectedURL = urlBase.String() + testCase.expectedPath
   226  			}
   227  
   228  			if buildURL != expectedURL {
   229  				t.Fatalf("%s: %q != %q", testCase.description, buildURL, expectedURL)
   230  			}
   231  		}
   232  	}
   233  }
   234  
   235  func TestBuilderFromRequestWithPrefix(t *testing.T) {
   236  	u, err := url.Parse("http://example.com/prefix/v2/")
   237  	if err != nil {
   238  		t.Fatal(err)
   239  	}
   240  
   241  	forwardedProtoHeader := make(http.Header, 1)
   242  	forwardedProtoHeader.Set("X-Forwarded-Proto", "https")
   243  
   244  	testRequests := []struct {
   245  		request    *http.Request
   246  		base       string
   247  		configHost url.URL
   248  	}{
   249  		{
   250  			request: &http.Request{URL: u, Host: u.Host},
   251  			base:    "http://example.com/prefix/",
   252  		},
   253  
   254  		{
   255  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
   256  			base:    "http://example.com/prefix/",
   257  		},
   258  		{
   259  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
   260  			base:    "https://example.com/prefix/",
   261  		},
   262  		{
   263  			request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
   264  			base:    "https://subdomain.example.com/prefix/",
   265  			configHost: url.URL{
   266  				Scheme: "https",
   267  				Host:   "subdomain.example.com",
   268  				Path:   "/prefix/",
   269  			},
   270  		},
   271  	}
   272  
   273  	for _, tr := range testRequests {
   274  		var builder *URLBuilder
   275  		if tr.configHost.Scheme != "" && tr.configHost.Host != "" {
   276  			builder = NewURLBuilder(&tr.configHost)
   277  		} else {
   278  			builder = NewURLBuilderFromRequest(tr.request)
   279  		}
   280  
   281  		for _, testCase := range makeURLBuilderTestCases(builder) {
   282  			buildURL, err := testCase.build()
   283  			if err != nil {
   284  				t.Fatalf("%s: error building url: %v", testCase.description, err)
   285  			}
   286  			var expectedURL string
   287  			proto, ok := tr.request.Header["X-Forwarded-Proto"]
   288  			if !ok {
   289  				expectedURL = tr.base[0:len(tr.base)-1] + testCase.expectedPath
   290  			} else {
   291  				urlBase, err := url.Parse(tr.base)
   292  				if err != nil {
   293  					t.Fatal(err)
   294  				}
   295  				urlBase.Scheme = proto[0]
   296  				expectedURL = urlBase.String()[0:len(urlBase.String())-1] + testCase.expectedPath
   297  			}
   298  
   299  			if buildURL != expectedURL {
   300  				t.Fatalf("%s: %q != %q", testCase.description, buildURL, expectedURL)
   301  			}
   302  		}
   303  	}
   304  }