github.com/cloudwego/hertz@v0.9.3/pkg/protocol/uri_test.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package protocol 43 44 import ( 45 "bytes" 46 "path/filepath" 47 "reflect" 48 "runtime" 49 "testing" 50 51 "github.com/cloudwego/hertz/pkg/common/test/assert" 52 ) 53 54 func TestURI_Username(t *testing.T) { 55 var req Request 56 req.SetRequestURI("http://user:pass@example.com/foo/bar") 57 u := req.URI() 58 user1 := string(u.Username()) 59 req.Header.SetRequestURIBytes([]byte("/foo/bar")) 60 u = req.URI() 61 user2 := string(u.Username()) 62 assert.DeepEqual(t, user1, user2) 63 64 expectUser3 := "user3" 65 expectUser4 := "user4" 66 67 u.SetUsername(expectUser3) 68 user3 := string(u.Username()) 69 assert.DeepEqual(t, expectUser3, user3) 70 u.SetUsername(expectUser4) 71 user4 := string(u.Username()) 72 assert.DeepEqual(t, expectUser4, user4) 73 74 u.SetUsernameBytes([]byte(user3)) 75 assert.DeepEqual(t, expectUser3, user3) 76 u.SetUsernameBytes([]byte(user4)) 77 assert.DeepEqual(t, expectUser4, user4) 78 } 79 80 func TestURI_Password(t *testing.T) { 81 u := AcquireURI() 82 defer ReleaseURI(u) 83 84 expectPassword1 := "password1" 85 expectPassword2 := "password2" 86 87 u.SetPassword(expectPassword1) 88 password1 := string(u.Password()) 89 assert.DeepEqual(t, expectPassword1, password1) 90 u.SetPassword(expectPassword2) 91 password2 := string(u.Password()) 92 assert.DeepEqual(t, expectPassword2, password2) 93 94 u.SetPasswordBytes([]byte(password1)) 95 assert.DeepEqual(t, expectPassword1, password1) 96 u.SetPasswordBytes([]byte(password2)) 97 assert.DeepEqual(t, expectPassword2, password2) 98 } 99 100 func TestURI_Hash(t *testing.T) { 101 u := AcquireURI() 102 defer ReleaseURI(u) 103 104 expectHash1 := "hash1" 105 expectHash2 := "hash2" 106 107 u.SetHash(expectHash1) 108 hash1 := string(u.Hash()) 109 assert.DeepEqual(t, expectHash1, hash1) 110 u.SetHash(expectHash2) 111 hash2 := string(u.Hash()) 112 assert.DeepEqual(t, expectHash2, hash2) 113 } 114 115 func TestURI_QueryString(t *testing.T) { 116 u := AcquireURI() 117 defer ReleaseURI(u) 118 119 expectQueryString1 := "key1=value1&key2=value2" 120 expectQueryString2 := "key3=value3&key4=value4" 121 122 u.SetQueryString(expectQueryString1) 123 queryString1 := string(u.QueryString()) 124 assert.DeepEqual(t, expectQueryString1, queryString1) 125 u.SetQueryString(expectQueryString2) 126 queryString2 := string(u.QueryString()) 127 assert.DeepEqual(t, expectQueryString2, queryString2) 128 } 129 130 func TestURI_Path(t *testing.T) { 131 u := AcquireURI() 132 defer ReleaseURI(u) 133 134 expectPath1 := "/" 135 expectPath2 := "/path1" 136 expectPath3 := "/path3" 137 138 // When Path is not set, Path defaults to "/" 139 path1 := string(u.Path()) 140 assert.DeepEqual(t, expectPath1, path1) 141 142 u.SetPath(expectPath2) 143 path2 := string(u.Path()) 144 assert.DeepEqual(t, expectPath2, path2) 145 u.SetPath(expectPath3) 146 path3 := string(u.Path()) 147 assert.DeepEqual(t, expectPath3, path3) 148 149 u.SetPathBytes([]byte(path2)) 150 assert.DeepEqual(t, expectPath2, path2) 151 u.SetPathBytes([]byte(path3)) 152 assert.DeepEqual(t, expectPath3, path3) 153 } 154 155 func TestURI_Scheme(t *testing.T) { 156 u := AcquireURI() 157 defer ReleaseURI(u) 158 159 expectScheme1 := "scheme1" 160 expectScheme2 := "scheme2" 161 162 u.SetScheme(expectScheme1) 163 scheme1 := string(u.Scheme()) 164 assert.DeepEqual(t, expectScheme1, scheme1) 165 u.SetScheme(expectScheme2) 166 scheme2 := string(u.Scheme()) 167 assert.DeepEqual(t, expectScheme2, scheme2) 168 169 u.SetSchemeBytes([]byte(scheme1)) 170 assert.DeepEqual(t, expectScheme1, scheme1) 171 u.SetSchemeBytes([]byte(scheme2)) 172 assert.DeepEqual(t, expectScheme2, scheme2) 173 } 174 175 func TestURI_Host(t *testing.T) { 176 u := AcquireURI() 177 defer ReleaseURI(u) 178 179 expectHost1 := "host1" 180 expectHost2 := "host2" 181 182 u.SetHost(expectHost1) 183 host1 := string(u.Host()) 184 assert.DeepEqual(t, expectHost1, host1) 185 u.SetHost(expectHost2) 186 host2 := string(u.Host()) 187 assert.DeepEqual(t, expectHost2, host2) 188 189 u.SetHostBytes([]byte(host1)) 190 assert.DeepEqual(t, expectHost1, host1) 191 u.SetHostBytes([]byte(host2)) 192 assert.DeepEqual(t, expectHost2, host2) 193 } 194 195 func TestURI_PathOriginal(t *testing.T) { 196 var u URI 197 expectPath := "/path" 198 u.Parse(nil, []byte(expectPath)) 199 uri := string(u.PathOriginal()) 200 assert.DeepEqual(t, expectPath, uri) 201 } 202 203 func TestArgsKV_Get(t *testing.T) { 204 var argsKV argsKV 205 expectKey := "key" 206 expectValue := "value" 207 argsKV.key = []byte(expectKey) 208 argsKV.value = []byte(expectValue) 209 key := string(argsKV.GetKey()) 210 value := string(argsKV.GetValue()) 211 assert.DeepEqual(t, expectKey, key) 212 assert.DeepEqual(t, expectValue, value) 213 } 214 215 func TestURICopyToQueryArgs(t *testing.T) { 216 t.Parallel() 217 218 var u URI 219 a := u.QueryArgs() 220 a.Set("foo", "bar") 221 222 var u1 URI 223 u.CopyTo(&u1) 224 a1 := u1.QueryArgs() 225 226 if string(a1.Peek("foo")) != "bar" { 227 t.Fatalf("unexpected query args value %q. Expecting %q", a1.Peek("foo"), "bar") 228 } 229 assert.DeepEqual(t, "bar", string(a1.Peek("foo"))) 230 } 231 232 func TestURICopyTo(t *testing.T) { 233 t.Parallel() 234 235 var u URI 236 var copyU URI 237 u.CopyTo(©U) 238 if !reflect.DeepEqual(&u, ©U) { //nolint:govet 239 t.Fatalf("URICopyTo fail, u: \n%+v\ncopyu: \n%+v\n", &u, ©U) //nolint:govet 240 } 241 242 u.UpdateBytes([]byte("https://google.com/foo?bar=baz&baraz#qqqq")) 243 u.CopyTo(©U) 244 if !reflect.DeepEqual(&u, ©U) { //nolint:govet 245 t.Fatalf("URICopyTo fail, u: \n%+v\ncopyu: \n%+v\n", &u, ©U) //nolint:govet 246 } 247 } 248 249 func TestURILastPathSegment(t *testing.T) { 250 t.Parallel() 251 252 testURILastPathSegment(t, "", "") 253 testURILastPathSegment(t, "/", "") 254 testURILastPathSegment(t, "/foo/bar/", "") 255 testURILastPathSegment(t, "/foobar.js", "foobar.js") 256 testURILastPathSegment(t, "/foo/bar/baz.html", "baz.html") 257 } 258 259 func testURILastPathSegment(t *testing.T, path, expectedSegment string) { 260 var u URI 261 u.SetPath(path) 262 segment := u.LastPathSegment() 263 assert.DeepEqual(t, expectedSegment, string(segment)) 264 } 265 266 func TestURIPathEscape(t *testing.T) { 267 t.Parallel() 268 269 testURIPathEscape(t, "/foo/bar", "/foo/bar") 270 testURIPathEscape(t, "/f_o-o=b:ar,b.c&q", "/f_o-o=b:ar,b.c&q") 271 testURIPathEscape(t, "/aa?bb.тест~qq", "/aa%3Fbb.%D1%82%D0%B5%D1%81%D1%82~qq") 272 } 273 274 func TestURIUpdate(t *testing.T) { 275 t.Parallel() 276 277 // full uri 278 testURIUpdate(t, "http://foo.bar/baz?aaa=22#aaa", "https://aaa.com/bb", "https://aaa.com/bb") 279 // empty uri 280 testURIUpdate(t, "http://aaa.com/aaa.html?234=234#add", "", "http://aaa.com/aaa.html?234=234#add") 281 282 // request uri 283 testURIUpdate(t, "ftp://aaa/xxx/yyy?aaa=bb#aa", "/boo/bar?xx", "ftp://aaa/boo/bar?xx") 284 285 // relative uri 286 testURIUpdate(t, "http://foo.bar/baz/xxx.html?aaa=22#aaa", "bb.html?xx=12#pp", "http://foo.bar/baz/bb.html?xx=12#pp") 287 testURIUpdate(t, "http://xx/a/b/c/d", "../qwe/p?zx=34", "http://xx/a/b/qwe/p?zx=34") 288 testURIUpdate(t, "https://qqq/aaa.html?foo=bar", "?baz=434&aaa#xcv", "https://qqq/aaa.html?baz=434&aaa#xcv") 289 testURIUpdate(t, "http://foo.bar/baz", "~a/%20b=c,тест?йцу=ке", "http://foo.bar/~a/%20b=c,%D1%82%D0%B5%D1%81%D1%82?йцу=ке") 290 testURIUpdate(t, "http://foo.bar/baz", "/qwe#fragment", "http://foo.bar/qwe#fragment") 291 testURIUpdate(t, "http://foobar/baz/xxx", "aaa.html#bb?cc=dd&ee=dfd", "http://foobar/baz/aaa.html#bb?cc=dd&ee=dfd") 292 293 // hash 294 testURIUpdate(t, "http://foo.bar/baz#aaa", "#fragment", "http://foo.bar/baz#fragment") 295 296 // uri without scheme 297 testURIUpdate(t, "https://foo.bar/baz", "//aaa.bbb/cc?dd", "https://aaa.bbb/cc?dd") 298 testURIUpdate(t, "http://foo.bar/baz", "//aaa.bbb/cc?dd", "http://aaa.bbb/cc?dd") 299 } 300 301 func testURIUpdate(t *testing.T, base, update, result string) { 302 var u URI 303 u.Parse(nil, []byte(base)) 304 u.Update(update) 305 s := u.String() 306 assert.DeepEqual(t, result, s) 307 } 308 309 func testURIPathEscape(t *testing.T, path, expectedRequestURI string) { 310 var u URI 311 u.SetPath(path) 312 requestURI := u.RequestURI() 313 assert.DeepEqual(t, expectedRequestURI, string(requestURI)) 314 } 315 316 func TestDelArgs(t *testing.T) { 317 var args Args 318 args.Set("foo", "bar") 319 assert.DeepEqual(t, string(args.Peek("foo")), "bar") 320 args.Del("foo") 321 assert.DeepEqual(t, string(args.Peek("foo")), "") 322 323 args.Set("foo2", "bar2") 324 assert.DeepEqual(t, string(args.Peek("foo2")), "bar2") 325 args.DelBytes([]byte("foo2")) 326 assert.DeepEqual(t, string(args.Peek("foo2")), "") 327 } 328 329 func TestURIFullURI(t *testing.T) { 330 t.Parallel() 331 332 var args Args 333 334 // empty scheme, path and hash 335 testURIFullURI(t, "", "foobar.com", "", "", &args, "http://foobar.com/") 336 337 // empty scheme and hash 338 testURIFullURI(t, "", "aaa.com", "/foo/bar", "", &args, "http://aaa.com/foo/bar") 339 // empty hash 340 testURIFullURI(t, "fTP", "XXx.com", "/foo", "", &args, "ftp://xxx.com/foo") 341 342 // empty args 343 testURIFullURI(t, "https", "xx.com", "/", "aaa", &args, "https://xx.com/#aaa") 344 345 // non-empty args and non-ASCII path 346 args.Set("foo", "bar") 347 args.Set("xxx", "йух") 348 testURIFullURI(t, "", "xxx.com", "/тест123", "2er", &args, "http://xxx.com/%D1%82%D0%B5%D1%81%D1%82123?foo=bar&xxx=%D0%B9%D1%83%D1%85#2er") 349 350 // test with empty args and non-empty query string 351 var u URI 352 u.Parse([]byte("google.com"), []byte("/foo?bar=baz&baraz#qqqq")) 353 uri := u.FullURI() 354 expectedURI := "http://google.com/foo?bar=baz&baraz#qqqq" 355 assert.DeepEqual(t, expectedURI, string(uri)) 356 } 357 358 func testURIFullURI(t *testing.T, scheme, host, path, hash string, args *Args, expectedURI string) { 359 var u URI 360 361 u.SetScheme(scheme) 362 u.SetHost(host) 363 u.SetPath(path) 364 u.SetHash(hash) 365 args.CopyTo(u.QueryArgs()) 366 367 uri := u.FullURI() 368 assert.DeepEqual(t, expectedURI, string(uri)) 369 } 370 371 func TestParsePathWindows(t *testing.T) { 372 t.Parallel() 373 374 testParsePathWindows(t, "/../../../../../foo", "/foo") 375 testParsePathWindows(t, "/..\\..\\..\\..\\..\\foo", "/foo") 376 testParsePathWindows(t, "/..%5c..%5cfoo", "/foo") 377 } 378 379 func TestURIPathNormalize(t *testing.T) { 380 if runtime.GOOS == "windows" { 381 t.SkipNow() 382 } 383 384 t.Parallel() 385 386 var u URI 387 388 // double slash 389 testURIPathNormalize(t, &u, "/aa//bb", "/aa/bb") 390 391 // triple slash 392 testURIPathNormalize(t, &u, "/x///y/", "/x/y/") 393 394 // multi slashes 395 testURIPathNormalize(t, &u, "/abc//de///fg////", "/abc/de/fg/") 396 397 // encoded slashes 398 testURIPathNormalize(t, &u, "/xxxx%2fyyy%2f%2F%2F", "/xxxx/yyy/") 399 400 // dotdot 401 testURIPathNormalize(t, &u, "/aaa/..", "/") 402 403 // dotdot with trailing slash 404 testURIPathNormalize(t, &u, "/xxx/yyy/../", "/xxx/") 405 406 // multi dotdots 407 testURIPathNormalize(t, &u, "/aaa/bbb/ccc/../../ddd", "/aaa/ddd") 408 409 // dotdots separated by other data 410 testURIPathNormalize(t, &u, "/a/b/../c/d/../e/..", "/a/c/") 411 412 // too many dotdots 413 testURIPathNormalize(t, &u, "/aaa/../../../../xxx", "/xxx") 414 testURIPathNormalize(t, &u, "/../../../../../..", "/") 415 testURIPathNormalize(t, &u, "/../../../../../../", "/") 416 417 // encoded dotdots 418 testURIPathNormalize(t, &u, "/aaa%2Fbbb%2F%2E.%2Fxxx", "/aaa/xxx") 419 420 // double slash with dotdots 421 testURIPathNormalize(t, &u, "/aaa////..//b", "/b") 422 423 // fake dotdot 424 testURIPathNormalize(t, &u, "/aaa/..bbb/ccc/..", "/aaa/..bbb/") 425 426 // single dot 427 testURIPathNormalize(t, &u, "/a/./b/././c/./d.html", "/a/b/c/d.html") 428 testURIPathNormalize(t, &u, "./foo/", "/foo/") 429 testURIPathNormalize(t, &u, "./../.././../../aaa/bbb/../../../././../", "/") 430 testURIPathNormalize(t, &u, "./a/./.././../b/./foo.html", "/b/foo.html") 431 } 432 433 func testURIPathNormalize(t *testing.T, u *URI, requestURI, expectedPath string) { 434 u.Parse(nil, []byte(requestURI)) //nolint:errcheck 435 if string(u.Path()) != expectedPath { 436 t.Fatalf("Unexpected path %q. Expected %q. requestURI=%q", u.Path(), expectedPath, requestURI) 437 } 438 } 439 440 func testParsePathWindows(t *testing.T, path, expectedPath string) { 441 var u URI 442 u.Parse(nil, []byte(path)) 443 parsedPath := u.Path() 444 if filepath.Separator == '\\' && string(parsedPath) != expectedPath { 445 t.Fatalf("Unexpected Path: %q. Expected %q", parsedPath, expectedPath) 446 } 447 } 448 449 func TestParseHostWithStr(t *testing.T) { 450 expectUsername := "username" 451 expectPassword := "password" 452 453 testParseHostWithStr(t, "username", "", "") 454 testParseHostWithStr(t, "username@", expectUsername, "") 455 testParseHostWithStr(t, "username:password@", expectUsername, expectPassword) 456 testParseHostWithStr(t, ":password@", "", expectPassword) 457 testParseHostWithStr(t, ":password", "", "") 458 } 459 460 func testParseHostWithStr(t *testing.T, host, expectUsername, expectPassword string) { 461 var u URI 462 u.Parse([]byte(host), nil) 463 assert.DeepEqual(t, expectUsername, string(u.Username())) 464 assert.DeepEqual(t, expectPassword, string(u.Password())) 465 } 466 467 func TestParseURI(t *testing.T) { 468 expectURI := "http://google.com/foo?bar=baz&baraz#qqqq" 469 uri := string(ParseURI(expectURI).FullURI()) 470 assert.DeepEqual(t, expectURI, uri) 471 } 472 473 func TestSplitHostURI(t *testing.T) { 474 cases := []struct { 475 host, uri []byte 476 wantScheme, wantHost, wantPath []byte 477 }{ 478 { 479 []byte("example.com"), []byte("/foobar"), 480 []byte("http"), []byte("example.com"), []byte("/foobar"), 481 }, 482 { 483 []byte("example2.com"), []byte("http://example2.com"), 484 []byte("http"), []byte("example2.com"), []byte("/"), 485 }, 486 { 487 []byte("example2.com"), []byte("http://example3.com"), 488 []byte("http"), []byte("example3.com"), []byte("/"), 489 }, 490 { 491 []byte("example3.com"), []byte("https://foobar.com?a=b"), 492 []byte("https"), []byte("foobar.com"), []byte("?a=b"), 493 }, 494 } 495 496 for _, c := range cases { 497 gotScheme, gotHost, gotPath := splitHostURI(c.host, c.uri) 498 if !bytes.Equal(gotScheme, c.wantScheme) || !bytes.Equal(gotHost, c.wantHost) || !bytes.Equal(gotPath, c.wantPath) { 499 t.Errorf("splitHostURI(%q, %q) == (%q, %q, %q), want (%q, %q, %q)", 500 c.host, c.uri, gotScheme, gotHost, gotPath, c.wantScheme, c.wantHost, c.wantPath) 501 } 502 } 503 }