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 }