github.com/cloudwego/hertz@v0.9.3/pkg/protocol/cookie_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  	"math/rand"
    46  	"strings"
    47  	"testing"
    48  	"time"
    49  
    50  	"github.com/cloudwego/hertz/pkg/common/test/assert"
    51  )
    52  
    53  func TestCookieAppendBytes(t *testing.T) {
    54  	t.Parallel()
    55  
    56  	c := &Cookie{}
    57  
    58  	testCookieAppendBytes(t, c, "", "bar", "bar")
    59  	testCookieAppendBytes(t, c, "foo", "", "foo=")
    60  	testCookieAppendBytes(t, c, "ффф", "12 лодлы", "ффф=12 лодлы")
    61  
    62  	c.SetDomain("foobar.com")
    63  	testCookieAppendBytes(t, c, "a", "b", "a=b; domain=foobar.com")
    64  
    65  	c.SetPath("/a/b")
    66  	testCookieAppendBytes(t, c, "aa", "bb", "aa=bb; domain=foobar.com; path=/a/b")
    67  
    68  	c.SetExpire(CookieExpireDelete)
    69  	testCookieAppendBytes(t, c, "xxx", "yyy", "xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b")
    70  
    71  	c.SetPartitioned(true)
    72  	testCookieAppendBytes(t, c, "xxx", "yyy", "xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b; secure; Partitioned")
    73  }
    74  
    75  func testCookieAppendBytes(t *testing.T, c *Cookie, key, value, expectedS string) {
    76  	c.SetKey(key)
    77  	c.SetValue(value)
    78  	result := string(c.AppendBytes(nil))
    79  	if result != expectedS {
    80  		t.Fatalf("Unexpected cookie %q. Expecting %q", result, expectedS)
    81  	}
    82  }
    83  
    84  func TestParseRequestCookies(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	testParseRequestCookies(t, "", "")
    88  	testParseRequestCookies(t, "=", "")
    89  	testParseRequestCookies(t, "foo", "foo")
    90  	testParseRequestCookies(t, "=foo", "foo")
    91  	testParseRequestCookies(t, "bar=", "bar=")
    92  	testParseRequestCookies(t, "xxx=aa;bb=c; =d; ;;e=g", "xxx=aa; bb=c; d; e=g")
    93  	testParseRequestCookies(t, "a;b;c; d=1;d=2", "a; b; c; d=1; d=2")
    94  	testParseRequestCookies(t, "   %D0%B8%D0%B2%D0%B5%D1%82=a%20b%3Bc   ;s%20s=aaa  ", "%D0%B8%D0%B2%D0%B5%D1%82=a%20b%3Bc; s%20s=aaa")
    95  }
    96  
    97  func testParseRequestCookies(t *testing.T, s, expectedS string) {
    98  	cookies := parseRequestCookies(nil, []byte(s))
    99  	ss := string(appendRequestCookieBytes(nil, cookies))
   100  	if ss != expectedS {
   101  		t.Fatalf("Unexpected cookies after parsing: %q. Expecting %q. String to parse %q", ss, expectedS, s)
   102  	}
   103  }
   104  
   105  func TestAppendRequestCookieBytes(t *testing.T) {
   106  	t.Parallel()
   107  
   108  	testAppendRequestCookieBytes(t, "=", "")
   109  	testAppendRequestCookieBytes(t, "foo=", "foo=")
   110  	testAppendRequestCookieBytes(t, "=bar", "bar")
   111  	testAppendRequestCookieBytes(t, "привет=a bc&s s=aaa", "привет=a bc; s s=aaa")
   112  }
   113  
   114  func testAppendRequestCookieBytes(t *testing.T, s, expectedS string) {
   115  	kvs := strings.Split(s, "&")
   116  	cookies := make([]argsKV, 0, len(kvs))
   117  	for _, ss := range kvs {
   118  		tmp := strings.SplitN(ss, "=", 2)
   119  		if len(tmp) != 2 {
   120  			t.Fatalf("Cannot find '=' in %q, part of %q", ss, s)
   121  		}
   122  		cookies = append(cookies, argsKV{
   123  			key:   []byte(tmp[0]),
   124  			value: []byte(tmp[1]),
   125  		})
   126  	}
   127  
   128  	prefix := "foobar"
   129  	result := string(appendRequestCookieBytes([]byte(prefix), cookies))
   130  	if result[:len(prefix)] != prefix {
   131  		t.Fatalf("unexpected prefix %q. Expecting %q for cookie %q", result[:len(prefix)], prefix, s)
   132  	}
   133  	result = result[len(prefix):]
   134  	if result != expectedS {
   135  		t.Fatalf("Unexpected result %q. Expecting %q for cookie %q", result, expectedS, s)
   136  	}
   137  }
   138  
   139  func TestCookieSecureHTTPOnly(t *testing.T) {
   140  	t.Parallel()
   141  
   142  	var c Cookie
   143  
   144  	if err := c.Parse("foo=bar; HttpOnly; secure"); err != nil {
   145  		t.Fatalf("unexpected error: %s", err)
   146  	}
   147  	if !c.Secure() {
   148  		t.Fatalf("secure must be set")
   149  	}
   150  	if !c.HTTPOnly() {
   151  		t.Fatalf("HttpOnly must be set")
   152  	}
   153  	s := c.String()
   154  	if !strings.Contains(s, "; secure") {
   155  		t.Fatalf("missing secure flag in cookie %q", s)
   156  	}
   157  	if !strings.Contains(s, "; HttpOnly") {
   158  		t.Fatalf("missing HttpOnly flag in cookie %q", s)
   159  	}
   160  }
   161  
   162  func TestCookieSecure(t *testing.T) {
   163  	t.Parallel()
   164  
   165  	var c Cookie
   166  
   167  	if err := c.Parse("foo=bar; secure"); err != nil {
   168  		t.Fatalf("unexpected error: %s", err)
   169  	}
   170  	if !c.Secure() {
   171  		t.Fatalf("secure must be set")
   172  	}
   173  	s := c.String()
   174  	if !strings.Contains(s, "; secure") {
   175  		t.Fatalf("missing secure flag in cookie %q", s)
   176  	}
   177  
   178  	if err := c.Parse("foo=bar"); err != nil {
   179  		t.Fatalf("unexpected error: %s", err)
   180  	}
   181  	if c.Secure() {
   182  		t.Fatalf("Unexpected secure flag set")
   183  	}
   184  	s = c.String()
   185  	if strings.Contains(s, "secure") {
   186  		t.Fatalf("unexpected secure flag in cookie %q", s)
   187  	}
   188  }
   189  
   190  func TestCookieSameSite(t *testing.T) {
   191  	t.Parallel()
   192  
   193  	var c Cookie
   194  
   195  	if err := c.Parse("foo=bar; samesite"); err != nil {
   196  		t.Fatalf("unexpected error: %s", err)
   197  	}
   198  	if c.SameSite() != CookieSameSiteDefaultMode {
   199  		t.Fatalf("SameSite must be set")
   200  	}
   201  	s := c.String()
   202  	if !strings.Contains(s, "; SameSite") {
   203  		t.Fatalf("missing SameSite flag in cookie %q", s)
   204  	}
   205  
   206  	if err := c.Parse("foo=bar; samesite=lax"); err != nil {
   207  		t.Fatalf("unexpected error: %s", err)
   208  	}
   209  	if c.SameSite() != CookieSameSiteLaxMode {
   210  		t.Fatalf("SameSite Lax Mode must be set")
   211  	}
   212  	s = c.String()
   213  	if !strings.Contains(s, "; SameSite=Lax") {
   214  		t.Fatalf("missing SameSite flag in cookie %q", s)
   215  	}
   216  
   217  	if err := c.Parse("foo=bar; samesite=strict"); err != nil {
   218  		t.Fatalf("unexpected error: %s", err)
   219  	}
   220  	if c.SameSite() != CookieSameSiteStrictMode {
   221  		t.Fatalf("SameSite Strict Mode must be set")
   222  	}
   223  	s = c.String()
   224  	if !strings.Contains(s, "; SameSite=Strict") {
   225  		t.Fatalf("missing SameSite flag in cookie %q", s)
   226  	}
   227  
   228  	if err := c.Parse("foo=bar; samesite=none"); err != nil {
   229  		t.Fatalf("unexpected error: %s", err)
   230  	}
   231  	if c.SameSite() != CookieSameSiteNoneMode {
   232  		t.Fatalf("SameSite None Mode must be set")
   233  	}
   234  	s = c.String()
   235  	if !strings.Contains(s, "; SameSite=None") {
   236  		t.Fatalf("missing SameSite flag in cookie %q", s)
   237  	}
   238  
   239  	if err := c.Parse("foo=bar"); err != nil {
   240  		t.Fatalf("unexpected error: %s", err)
   241  	}
   242  	c.SetSameSite(CookieSameSiteNoneMode)
   243  	s = c.String()
   244  	if !strings.Contains(s, "; SameSite=None") {
   245  		t.Fatalf("missing SameSite flag in cookie %q", s)
   246  	}
   247  	if !strings.Contains(s, "; secure") {
   248  		t.Fatalf("missing Secure flag in cookie %q", s)
   249  	}
   250  
   251  	if err := c.Parse("foo=bar"); err != nil {
   252  		t.Fatalf("unexpected error: %s", err)
   253  	}
   254  	if c.SameSite() != CookieSameSiteDisabled {
   255  		t.Fatalf("Unexpected SameSite flag set")
   256  	}
   257  	s = c.String()
   258  	if strings.Contains(s, "SameSite") {
   259  		t.Fatalf("unexpected SameSite flag in cookie %q", s)
   260  	}
   261  }
   262  
   263  func TestCookiePartitioned(t *testing.T) {
   264  	t.Parallel()
   265  
   266  	var c Cookie
   267  
   268  	if err := c.Parse("__Host-name=value; Secure; Path=/; SameSite=None; Partitioned;"); err != nil {
   269  		t.Fatalf("unexpected error for valid paritionedd cookie: %s", err)
   270  	}
   271  	if !c.Partitioned() {
   272  		t.Fatalf("partitioned must be set")
   273  	}
   274  
   275  	if err := c.Parse("foo=bar"); err != nil {
   276  		t.Fatalf("unexpected error: %s", err)
   277  	}
   278  	c.SetPartitioned(true)
   279  	s := c.String()
   280  	if !strings.Contains(s, "; Partitioned") {
   281  		t.Fatalf("missing Partitioned flag in cookie %q", s)
   282  	}
   283  	if !strings.Contains(s, "; secure") {
   284  		t.Fatalf("missing Secure flag in cookie %q", s)
   285  	}
   286  }
   287  
   288  func TestCookieMaxAge(t *testing.T) {
   289  	t.Parallel()
   290  
   291  	var c Cookie
   292  
   293  	maxAge := 100
   294  	if err := c.Parse("foo=bar; max-age=100"); err != nil {
   295  		t.Fatalf("unexpected error: %s", err)
   296  	}
   297  	if maxAge != c.MaxAge() {
   298  		t.Fatalf("max-age must be set")
   299  	}
   300  	s := c.String()
   301  	if !strings.Contains(s, "; max-age=100") {
   302  		t.Fatalf("missing max-age flag in cookie %q", s)
   303  	}
   304  
   305  	if err := c.Parse("foo=bar; expires=Tue, 10 Nov 2009 23:00:00 GMT; max-age=100;"); err != nil {
   306  		t.Fatalf("unexpected error: %s", err)
   307  	}
   308  	if maxAge != c.MaxAge() {
   309  		t.Fatalf("max-age ignored")
   310  	}
   311  	s = c.String()
   312  	if s != "foo=bar; max-age=100" {
   313  		t.Fatalf("missing max-age in cookie %q", s)
   314  	}
   315  
   316  	expires := time.Unix(100, 0)
   317  	c.SetExpire(expires)
   318  	s = c.String()
   319  	if s != "foo=bar; max-age=100" {
   320  		t.Fatalf("expires should be ignored due to max-age: %q", s)
   321  	}
   322  
   323  	c.SetMaxAge(0)
   324  	s = c.String()
   325  	if s != "foo=bar; expires=Thu, 01 Jan 1970 00:01:40 GMT" {
   326  		t.Fatalf("missing expires %q", s)
   327  	}
   328  }
   329  
   330  func TestCookieHttpOnly(t *testing.T) {
   331  	t.Parallel()
   332  
   333  	var c Cookie
   334  
   335  	if err := c.Parse("foo=bar; HttpOnly"); err != nil {
   336  		t.Fatalf("unexpected error: %s", err)
   337  	}
   338  	if !c.HTTPOnly() {
   339  		t.Fatalf("HTTPOnly must be set")
   340  	}
   341  	s := c.String()
   342  	if !strings.Contains(s, "; HttpOnly") {
   343  		t.Fatalf("missing HttpOnly flag in cookie %q", s)
   344  	}
   345  
   346  	if err := c.Parse("foo=bar"); err != nil {
   347  		t.Fatalf("unexpected error: %s", err)
   348  	}
   349  	if c.HTTPOnly() {
   350  		t.Fatalf("Unexpected HTTPOnly flag set")
   351  	}
   352  	s = c.String()
   353  	if strings.Contains(s, "HttpOnly") {
   354  		t.Fatalf("unexpected HttpOnly flag in cookie %q", s)
   355  	}
   356  }
   357  
   358  func TestCookieParse(t *testing.T) {
   359  	t.Parallel()
   360  
   361  	testCookieParse(t, "foo", "foo")
   362  	testCookieParse(t, "foo=bar", "foo=bar")
   363  	testCookieParse(t, "foo=", "foo=")
   364  	testCookieParse(t, `foo="bar"`, "foo=bar")
   365  	testCookieParse(t, `"foo"=bar`, `"foo"=bar`)
   366  	testCookieParse(t, "foo=bar; Domain=aaa.com; PATH=/foo/bar", "foo=bar; domain=aaa.com; path=/foo/bar")
   367  	testCookieParse(t, "foo=bar; max-age= 101 ; expires= Tue, 10 Nov 2009 23:00:00 GMT", "foo=bar; max-age=101")
   368  	testCookieParse(t, " xxx = yyy  ; path=/a/b;;;domain=foobar.com ; expires= Tue, 10 Nov 2009 23:00:00 GMT ; ;;",
   369  		"xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b")
   370  }
   371  
   372  func Test_decodeCookieArg(t *testing.T) {
   373  	src := []byte("          \"aaaaabbbbb\"         ")
   374  	dst := make([]byte, 0)
   375  	dst = decodeCookieArg(dst, src, true)
   376  	assert.DeepEqual(t, []byte("aaaaabbbbb"), dst)
   377  }
   378  
   379  func testCookieParse(t *testing.T, s, expectedS string) {
   380  	var c Cookie
   381  	if err := c.Parse(s); err != nil {
   382  		t.Fatalf("unexpected error: %s", err)
   383  	}
   384  	result := string(c.Cookie())
   385  	if result != expectedS {
   386  		t.Fatalf("unexpected cookies %q. Expecting %q. Original %q", result, expectedS, s)
   387  	}
   388  }
   389  
   390  func Test_WarnIfInvalid(t *testing.T) {
   391  	assert.False(t, warnIfInvalid([]byte(";")))
   392  	assert.False(t, warnIfInvalid([]byte("\\")))
   393  	assert.False(t, warnIfInvalid([]byte("\"")))
   394  	assert.True(t, warnIfInvalid([]byte("")))
   395  	for i := 0; i < 5; i++ {
   396  		validCookie := getValidCookie()
   397  		assert.True(t, warnIfInvalid(validCookie))
   398  	}
   399  }
   400  
   401  func getValidCookie() []byte {
   402  	var validCookie []byte
   403  	for i := 0; i < 100; i++ {
   404  		r := rand.Intn(0x78-0x20) + 0x20
   405  		if r == ';' || r == '\\' || r == '"' {
   406  			continue
   407  		}
   408  		validCookie = append(validCookie, byte(r))
   409  	}
   410  	return validCookie
   411  }