github.com/cloudwego/hertz@v0.9.3/pkg/protocol/args.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  	"io"
    47  
    48  	"github.com/cloudwego/hertz/internal/bytesconv"
    49  	"github.com/cloudwego/hertz/internal/nocopy"
    50  )
    51  
    52  const (
    53  	argsNoValue  = true
    54  	ArgsHasValue = false
    55  )
    56  
    57  var nilByteSlice = []byte{}
    58  
    59  type argsScanner struct {
    60  	b []byte
    61  }
    62  
    63  type Args struct {
    64  	noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used
    65  
    66  	args []argsKV
    67  	buf  []byte
    68  }
    69  
    70  // Set sets 'key=value' argument.
    71  func (a *Args) Set(key, value string) {
    72  	a.args = setArg(a.args, key, value, ArgsHasValue)
    73  }
    74  
    75  // Reset clears query args.
    76  func (a *Args) Reset() {
    77  	a.args = a.args[:0]
    78  }
    79  
    80  // CopyTo copies all args to dst.
    81  func (a *Args) CopyTo(dst *Args) {
    82  	dst.Reset()
    83  	dst.args = copyArgs(dst.args, a.args)
    84  }
    85  
    86  // Del deletes argument with the given key from query args.
    87  func (a *Args) Del(key string) {
    88  	a.args = delAllArgs(a.args, key)
    89  }
    90  
    91  // DelBytes deletes argument with the given key from query args.
    92  func (a *Args) DelBytes(key []byte) {
    93  	a.args = delAllArgs(a.args, bytesconv.B2s(key))
    94  }
    95  
    96  func (s *argsScanner) next(kv *argsKV) bool {
    97  	if len(s.b) == 0 {
    98  		return false
    99  	}
   100  	kv.noValue = ArgsHasValue
   101  
   102  	isKey := true
   103  	k := 0
   104  	for i, c := range s.b {
   105  		switch c {
   106  		case '=':
   107  			if isKey {
   108  				isKey = false
   109  				kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
   110  				k = i + 1
   111  			}
   112  		case '&':
   113  			if isKey {
   114  				kv.key = decodeArgAppend(kv.key[:0], s.b[:i])
   115  				kv.value = kv.value[:0]
   116  				kv.noValue = argsNoValue
   117  			} else {
   118  				kv.value = decodeArgAppend(kv.value[:0], s.b[k:i])
   119  			}
   120  			s.b = s.b[i+1:]
   121  			return true
   122  		}
   123  	}
   124  
   125  	if isKey {
   126  		kv.key = decodeArgAppend(kv.key[:0], s.b)
   127  		kv.value = kv.value[:0]
   128  		kv.noValue = argsNoValue
   129  	} else {
   130  		kv.value = decodeArgAppend(kv.value[:0], s.b[k:])
   131  	}
   132  	s.b = s.b[len(s.b):]
   133  	return true
   134  }
   135  
   136  func decodeArgAppend(dst, src []byte) []byte {
   137  	if bytes.IndexByte(src, '%') < 0 && bytes.IndexByte(src, '+') < 0 {
   138  		// fast path: src doesn't contain encoded chars
   139  		return append(dst, src...)
   140  	}
   141  
   142  	// slow path
   143  	for i := 0; i < len(src); i++ {
   144  		c := src[i]
   145  		if c == '%' {
   146  			if i+2 >= len(src) {
   147  				return append(dst, src[i:]...)
   148  			}
   149  			x2 := bytesconv.Hex2intTable[src[i+2]]
   150  			x1 := bytesconv.Hex2intTable[src[i+1]]
   151  			if x1 == 16 || x2 == 16 {
   152  				dst = append(dst, '%')
   153  			} else {
   154  				dst = append(dst, x1<<4|x2)
   155  				i += 2
   156  			}
   157  		} else if c == '+' {
   158  			dst = append(dst, ' ')
   159  		} else {
   160  			dst = append(dst, c)
   161  		}
   162  	}
   163  	return dst
   164  }
   165  
   166  func allocArg(h []argsKV) ([]argsKV, *argsKV) {
   167  	n := len(h)
   168  	if cap(h) > n {
   169  		h = h[:n+1]
   170  	} else {
   171  		h = append(h, argsKV{})
   172  	}
   173  	return h, &h[n]
   174  }
   175  
   176  func releaseArg(h []argsKV) []argsKV {
   177  	return h[:len(h)-1]
   178  }
   179  
   180  func updateArgBytes(h []argsKV, key, value []byte) []argsKV {
   181  	n := len(h)
   182  	for i := 0; i < n; i++ {
   183  		kv := &h[i]
   184  		if kv.noValue && bytes.Equal(key, kv.key) {
   185  			kv.value = append(kv.value[:0], value...)
   186  			kv.noValue = ArgsHasValue
   187  			return h
   188  		}
   189  	}
   190  	return h
   191  }
   192  
   193  func setArgBytes(h []argsKV, key, value []byte, noValue bool) []argsKV {
   194  	n := len(h)
   195  	for i := 0; i < n; i++ {
   196  		kv := &h[i]
   197  		if bytes.Equal(key, kv.key) {
   198  			if noValue {
   199  				kv.value = kv.value[:0]
   200  			} else {
   201  				kv.value = append(kv.value[:0], value...)
   202  			}
   203  			kv.noValue = noValue
   204  			return h
   205  		}
   206  	}
   207  	return appendArgBytes(h, key, value, noValue)
   208  }
   209  
   210  func setArg(h []argsKV, key, value string, noValue bool) []argsKV {
   211  	n := len(h)
   212  	for i := 0; i < n; i++ {
   213  		kv := &h[i]
   214  		if key == string(kv.key) {
   215  			if noValue {
   216  				kv.value = kv.value[:0]
   217  			} else {
   218  				kv.value = append(kv.value[:0], value...)
   219  			}
   220  			kv.noValue = noValue
   221  			return h
   222  		}
   223  	}
   224  	return appendArg(h, key, value, noValue)
   225  }
   226  
   227  func peekArgBytes(h []argsKV, k []byte) []byte {
   228  	for i, n := 0, len(h); i < n; i++ {
   229  		kv := &h[i]
   230  		if bytes.Equal(kv.key, k) {
   231  			if kv.value != nil {
   232  				return kv.value
   233  			}
   234  			return nilByteSlice
   235  		}
   236  	}
   237  	return nil
   238  }
   239  
   240  func peekAllArgBytesToDst(dst [][]byte, h []argsKV, k []byte) [][]byte {
   241  	for i, n := 0, len(h); i < n; i++ {
   242  		kv := &h[i]
   243  		if bytes.Equal(kv.key, k) {
   244  			dst = append(dst, kv.value)
   245  		}
   246  	}
   247  	return dst
   248  }
   249  
   250  func delAllArgsBytes(args []argsKV, key []byte) []argsKV {
   251  	return delAllArgs(args, bytesconv.B2s(key))
   252  }
   253  
   254  func delAllArgs(args []argsKV, key string) []argsKV {
   255  	for i, n := 0, len(args); i < n; i++ {
   256  		kv := &args[i]
   257  		if key == string(kv.key) {
   258  			tmp := *kv
   259  			copy(args[i:], args[i+1:])
   260  			n--
   261  			i--
   262  			args[n] = tmp
   263  			args = args[:n]
   264  		}
   265  	}
   266  	return args
   267  }
   268  
   269  // Has returns true if the given key exists in Args.
   270  func (a *Args) Has(key string) bool {
   271  	return hasArg(a.args, key)
   272  }
   273  
   274  func hasArg(h []argsKV, key string) bool {
   275  	for i, n := 0, len(h); i < n; i++ {
   276  		kv := &h[i]
   277  		if key == string(kv.key) {
   278  			return true
   279  		}
   280  	}
   281  	return false
   282  }
   283  
   284  // String returns string representation of query args.
   285  func (a *Args) String() string {
   286  	return string(a.QueryString())
   287  }
   288  
   289  // decodeArgAppendNoPlus is almost identical to decodeArgAppend, but it doesn't
   290  // substitute '+' with ' '.
   291  //
   292  // The function is copy-pasted from decodeArgAppend due to the performance
   293  // reasons only.
   294  func decodeArgAppendNoPlus(dst, src []byte) []byte {
   295  	if bytes.IndexByte(src, '%') < 0 {
   296  		// fast path: src doesn't contain encoded chars
   297  		return append(dst, src...)
   298  	}
   299  
   300  	// slow path
   301  	for i := 0; i < len(src); i++ {
   302  		c := src[i]
   303  		if c == '%' {
   304  			if i+2 >= len(src) {
   305  				return append(dst, src[i:]...)
   306  			}
   307  			x2 := bytesconv.Hex2intTable[src[i+2]]
   308  			x1 := bytesconv.Hex2intTable[src[i+1]]
   309  			if x1 == 16 || x2 == 16 {
   310  				dst = append(dst, '%')
   311  			} else {
   312  				dst = append(dst, x1<<4|x2)
   313  				i += 2
   314  			}
   315  		} else {
   316  			dst = append(dst, c)
   317  		}
   318  	}
   319  	return dst
   320  }
   321  
   322  func peekArgStr(h []argsKV, k string) []byte {
   323  	for i, n := 0, len(h); i < n; i++ {
   324  		kv := &h[i]
   325  		if string(kv.key) == k {
   326  			return kv.value
   327  		}
   328  	}
   329  	return nil
   330  }
   331  
   332  func peekArgStrExists(h []argsKV, k string) (string, bool) {
   333  	for i, n := 0, len(h); i < n; i++ {
   334  		kv := &h[i]
   335  		if string(kv.key) == k {
   336  			return string(kv.value), true
   337  		}
   338  	}
   339  	return "", false
   340  }
   341  
   342  // QueryString returns query string for the args.
   343  //
   344  // The returned value is valid until the next call to Args methods.
   345  func (a *Args) QueryString() []byte {
   346  	a.buf = a.AppendBytes(a.buf[:0])
   347  	return a.buf
   348  }
   349  
   350  // ParseBytes parses the given b containing query args.
   351  func (a *Args) ParseBytes(b []byte) {
   352  	a.Reset()
   353  
   354  	var s argsScanner
   355  	s.b = b
   356  
   357  	var kv *argsKV
   358  	a.args, kv = allocArg(a.args)
   359  	for s.next(kv) {
   360  		if len(kv.key) > 0 || len(kv.value) > 0 {
   361  			a.args, kv = allocArg(a.args)
   362  		}
   363  	}
   364  	a.args = releaseArg(a.args)
   365  
   366  	if len(a.args) == 0 {
   367  		return
   368  	}
   369  }
   370  
   371  // Peek returns query arg value for the given key.
   372  //
   373  // Returned value is valid until the next Args call.
   374  func (a *Args) Peek(key string) []byte {
   375  	return peekArgStr(a.args, key)
   376  }
   377  
   378  func (a *Args) PeekExists(key string) (string, bool) {
   379  	return peekArgStrExists(a.args, key)
   380  }
   381  
   382  // PeekAll returns all the arg values for the given key.
   383  func (a *Args) PeekAll(key string) [][]byte {
   384  	var values [][]byte
   385  	a.VisitAll(func(k, v []byte) {
   386  		if bytesconv.B2s(k) == key {
   387  			values = append(values, v)
   388  		}
   389  	})
   390  	return values
   391  }
   392  
   393  func visitArgs(args []argsKV, f func(k, v []byte)) {
   394  	for i, n := 0, len(args); i < n; i++ {
   395  		kv := &args[i]
   396  		f(kv.key, kv.value)
   397  	}
   398  }
   399  
   400  // Len returns the number of query args.
   401  func (a *Args) Len() int {
   402  	return len(a.args)
   403  }
   404  
   405  // AppendBytes appends query string to dst and returns the extended dst.
   406  func (a *Args) AppendBytes(dst []byte) []byte {
   407  	for i, n := 0, len(a.args); i < n; i++ {
   408  		kv := &a.args[i]
   409  		dst = bytesconv.AppendQuotedArg(dst, kv.key)
   410  		if !kv.noValue {
   411  			dst = append(dst, '=')
   412  			if len(kv.value) > 0 {
   413  				dst = bytesconv.AppendQuotedArg(dst, kv.value)
   414  			}
   415  		}
   416  		if i+1 < n {
   417  			dst = append(dst, '&')
   418  		}
   419  	}
   420  	return dst
   421  }
   422  
   423  // VisitAll calls f for each existing arg.
   424  //
   425  // f must not retain references to key and value after returning.
   426  // Make key and/or value copies if you need storing them after returning.
   427  func (a *Args) VisitAll(f func(key, value []byte)) {
   428  	visitArgs(a.args, f)
   429  }
   430  
   431  // WriteTo writes query string to w.
   432  //
   433  // WriteTo implements io.WriterTo interface.
   434  func (a *Args) WriteTo(w io.Writer) (int64, error) {
   435  	n, err := w.Write(a.QueryString())
   436  	return int64(n), err
   437  }
   438  
   439  // Add adds 'key=value' argument.
   440  //
   441  // Multiple values for the same key may be added.
   442  func (a *Args) Add(key, value string) {
   443  	a.args = appendArg(a.args, key, value, ArgsHasValue)
   444  }