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 }