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(&copyU)
   238  	if !reflect.DeepEqual(&u, &copyU) { //nolint:govet
   239  		t.Fatalf("URICopyTo fail, u: \n%+v\ncopyu: \n%+v\n", &u, &copyU) //nolint:govet
   240  	}
   241  
   242  	u.UpdateBytes([]byte("https://google.com/foo?bar=baz&baraz#qqqq"))
   243  	u.CopyTo(&copyU)
   244  	if !reflect.DeepEqual(&u, &copyU) { //nolint:govet
   245  		t.Fatalf("URICopyTo fail, u: \n%+v\ncopyu: \n%+v\n", &u, &copyU) //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  }