github.com/cloudwego/hertz@v0.9.3/pkg/protocol/http1/ext/headerscanner.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 ext
    43  
    44  import (
    45  	"bytes"
    46  
    47  	errs "github.com/cloudwego/hertz/pkg/common/errors"
    48  	"github.com/cloudwego/hertz/pkg/common/utils"
    49  )
    50  
    51  var errInvalidName = errs.NewPublic("invalid header name")
    52  
    53  type HeaderScanner struct {
    54  	B     []byte
    55  	Key   []byte
    56  	Value []byte
    57  	Err   error
    58  
    59  	// HLen stores header subslice len
    60  	HLen int
    61  
    62  	DisableNormalizing bool
    63  
    64  	// by checking whether the Next line contains a colon or not to tell
    65  	// it's a header entry or a multi line value of current header entry.
    66  	// the side effect of this operation is that we know the index of the
    67  	// Next colon and new line, so this can be used during Next iteration,
    68  	// instead of find them again.
    69  	nextColon   int
    70  	nextNewLine int
    71  
    72  	initialized bool
    73  }
    74  
    75  type HeaderValueScanner struct {
    76  	B     []byte
    77  	Value []byte
    78  }
    79  
    80  func (s *HeaderScanner) Next() bool {
    81  	if !s.initialized {
    82  		s.nextColon = -1
    83  		s.nextNewLine = -1
    84  		s.initialized = true
    85  	}
    86  	bLen := len(s.B)
    87  	if bLen >= 2 && s.B[0] == '\r' && s.B[1] == '\n' {
    88  		s.B = s.B[2:]
    89  		s.HLen += 2
    90  		return false
    91  	}
    92  	if bLen >= 1 && s.B[0] == '\n' {
    93  		s.B = s.B[1:]
    94  		s.HLen++
    95  		return false
    96  	}
    97  	var n int
    98  	if s.nextColon >= 0 {
    99  		n = s.nextColon
   100  		s.nextColon = -1
   101  	} else {
   102  		n = bytes.IndexByte(s.B, ':')
   103  
   104  		// There can't be a \n inside the header name, check for this.
   105  		x := bytes.IndexByte(s.B, '\n')
   106  		if x < 0 {
   107  			// A header name should always at some point be followed by a \n
   108  			// even if it's the one that terminates the header block.
   109  			s.Err = errNeedMore
   110  			return false
   111  		}
   112  		if x < n {
   113  			// There was a \n before the :
   114  			s.Err = errInvalidName
   115  			return false
   116  		}
   117  	}
   118  	if n < 0 {
   119  		s.Err = errNeedMore
   120  		return false
   121  	}
   122  	s.Key = s.B[:n]
   123  	utils.NormalizeHeaderKey(s.Key, s.DisableNormalizing)
   124  	n++
   125  	for len(s.B) > n && s.B[n] == ' ' {
   126  		n++
   127  		// the newline index is a relative index, and lines below trimmed `s.b` by `n`,
   128  		// so the relative newline index also shifted forward. it's safe to decrease
   129  		// to a minus value, it means it's invalid, and will find the newline again.
   130  		s.nextNewLine--
   131  	}
   132  	s.HLen += n
   133  	s.B = s.B[n:]
   134  	if s.nextNewLine >= 0 {
   135  		n = s.nextNewLine
   136  		s.nextNewLine = -1
   137  	} else {
   138  		n = bytes.IndexByte(s.B, '\n')
   139  	}
   140  	if n < 0 {
   141  		s.Err = errNeedMore
   142  		return false
   143  	}
   144  	isMultiLineValue := false
   145  	for {
   146  		if n+1 >= len(s.B) {
   147  			break
   148  		}
   149  		if s.B[n+1] != ' ' && s.B[n+1] != '\t' {
   150  			break
   151  		}
   152  		d := bytes.IndexByte(s.B[n+1:], '\n')
   153  		if d <= 0 {
   154  			break
   155  		} else if d == 1 && s.B[n+1] == '\r' {
   156  			break
   157  		}
   158  		e := n + d + 1
   159  		if c := bytes.IndexByte(s.B[n+1:e], ':'); c >= 0 {
   160  			s.nextColon = c
   161  			s.nextNewLine = d - c - 1
   162  			break
   163  		}
   164  		isMultiLineValue = true
   165  		n = e
   166  	}
   167  	if n >= len(s.B) {
   168  		s.Err = errNeedMore
   169  		return false
   170  	}
   171  	oldB := s.B
   172  	s.Value = s.B[:n]
   173  	s.HLen += n + 1
   174  	s.B = s.B[n+1:]
   175  
   176  	if n > 0 && s.Value[n-1] == '\r' {
   177  		n--
   178  	}
   179  	for n > 0 && s.Value[n-1] == ' ' {
   180  		n--
   181  	}
   182  	s.Value = s.Value[:n]
   183  	if isMultiLineValue {
   184  		s.Value, s.B, s.HLen = normalizeHeaderValue(s.Value, oldB, s.HLen)
   185  	}
   186  	return true
   187  }
   188  
   189  func (s *HeaderValueScanner) next() bool {
   190  	b := s.B
   191  	if len(b) == 0 {
   192  		return false
   193  	}
   194  	n := bytes.IndexByte(b, ',')
   195  	if n < 0 {
   196  		s.Value = stripSpace(b)
   197  		s.B = b[len(b):]
   198  		return true
   199  	}
   200  	s.Value = stripSpace(b[:n])
   201  	s.B = b[n+1:]
   202  	return true
   203  }
   204  
   205  func HasHeaderValue(s, value []byte) bool {
   206  	var vs HeaderValueScanner
   207  	vs.B = s
   208  	for vs.next() {
   209  		if utils.CaseInsensitiveCompare(vs.Value, value) {
   210  			return true
   211  		}
   212  	}
   213  	return false
   214  }