github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/internal/transport/http_util.go (about) 1 /* 2 * 3 * Copyright 2014 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package transport 20 21 import ( 22 "bufio" 23 "bytes" 24 "encoding/base64" 25 "fmt" 26 "io" 27 "math" 28 "net" 29 "net/url" 30 "strconv" 31 "strings" 32 "time" 33 "unicode/utf8" 34 35 http "github.com/hxx258456/ccgo/gmhttp" 36 37 "github.com/golang/protobuf/proto" 38 "github.com/hxx258456/ccgo/grpc/codes" 39 "github.com/hxx258456/ccgo/grpc/grpclog" 40 "github.com/hxx258456/ccgo/grpc/status" 41 "github.com/hxx258456/ccgo/net/http2" 42 "github.com/hxx258456/ccgo/net/http2/hpack" 43 spb "google.golang.org/genproto/googleapis/rpc/status" 44 ) 45 46 const ( 47 // http2MaxFrameLen specifies the max length of a HTTP2 frame. 48 http2MaxFrameLen = 16384 // 16KB frame 49 // http://http2.github.io/http2-spec/#SettingValues 50 http2InitHeaderTableSize = 4096 51 // baseContentType is the base content-type for gRPC. This is a valid 52 // content-type on it's own, but can also include a content-subtype such as 53 // "proto" as a suffix after "+" or ";". See 54 // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests 55 // for more details. 56 57 ) 58 59 var ( 60 clientPreface = []byte(http2.ClientPreface) 61 http2ErrConvTab = map[http2.ErrCode]codes.Code{ 62 http2.ErrCodeNo: codes.Internal, 63 http2.ErrCodeProtocol: codes.Internal, 64 http2.ErrCodeInternal: codes.Internal, 65 http2.ErrCodeFlowControl: codes.ResourceExhausted, 66 http2.ErrCodeSettingsTimeout: codes.Internal, 67 http2.ErrCodeStreamClosed: codes.Internal, 68 http2.ErrCodeFrameSize: codes.Internal, 69 http2.ErrCodeRefusedStream: codes.Unavailable, 70 http2.ErrCodeCancel: codes.Canceled, 71 http2.ErrCodeCompression: codes.Internal, 72 http2.ErrCodeConnect: codes.Internal, 73 http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted, 74 http2.ErrCodeInadequateSecurity: codes.PermissionDenied, 75 http2.ErrCodeHTTP11Required: codes.Internal, 76 } 77 // HTTPStatusConvTab is the HTTP status code to gRPC error code conversion table. 78 HTTPStatusConvTab = map[int]codes.Code{ 79 // 400 Bad Request - INTERNAL. 80 http.StatusBadRequest: codes.Internal, 81 // 401 Unauthorized - UNAUTHENTICATED. 82 http.StatusUnauthorized: codes.Unauthenticated, 83 // 403 Forbidden - PERMISSION_DENIED. 84 http.StatusForbidden: codes.PermissionDenied, 85 // 404 Not Found - UNIMPLEMENTED. 86 http.StatusNotFound: codes.Unimplemented, 87 // 429 Too Many Requests - UNAVAILABLE. 88 http.StatusTooManyRequests: codes.Unavailable, 89 // 502 Bad Gateway - UNAVAILABLE. 90 http.StatusBadGateway: codes.Unavailable, 91 // 503 Service Unavailable - UNAVAILABLE. 92 http.StatusServiceUnavailable: codes.Unavailable, 93 // 504 Gateway timeout - UNAVAILABLE. 94 http.StatusGatewayTimeout: codes.Unavailable, 95 } 96 logger = grpclog.Component("transport") 97 ) 98 99 // isReservedHeader checks whether hdr belongs to HTTP2 headers 100 // reserved by gRPC protocol. Any other headers are classified as the 101 // user-specified metadata. 102 func isReservedHeader(hdr string) bool { 103 if hdr != "" && hdr[0] == ':' { 104 return true 105 } 106 switch hdr { 107 case "content-type", 108 "user-agent", 109 "grpc-message-type", 110 "grpc-encoding", 111 "grpc-message", 112 "grpc-status", 113 "grpc-timeout", 114 "grpc-status-details-bin", 115 // Intentionally exclude grpc-previous-rpc-attempts and 116 // grpc-retry-pushback-ms, which are "reserved", but their API 117 // intentionally works via metadata. 118 "te": 119 return true 120 default: 121 return false 122 } 123 } 124 125 // isWhitelistedHeader checks whether hdr should be propagated into metadata 126 // visible to users, even though it is classified as "reserved", above. 127 func isWhitelistedHeader(hdr string) bool { 128 switch hdr { 129 case ":authority", "user-agent": 130 return true 131 default: 132 return false 133 } 134 } 135 136 const binHdrSuffix = "-bin" 137 138 func encodeBinHeader(v []byte) string { 139 return base64.RawStdEncoding.EncodeToString(v) 140 } 141 142 func decodeBinHeader(v string) ([]byte, error) { 143 if len(v)%4 == 0 { 144 // Input was padded, or padding was not necessary. 145 return base64.StdEncoding.DecodeString(v) 146 } 147 return base64.RawStdEncoding.DecodeString(v) 148 } 149 150 func encodeMetadataHeader(k, v string) string { 151 if strings.HasSuffix(k, binHdrSuffix) { 152 return encodeBinHeader(([]byte)(v)) 153 } 154 return v 155 } 156 157 func decodeMetadataHeader(k, v string) (string, error) { 158 if strings.HasSuffix(k, binHdrSuffix) { 159 b, err := decodeBinHeader(v) 160 return string(b), err 161 } 162 return v, nil 163 } 164 165 func decodeGRPCStatusDetails(rawDetails string) (*status.Status, error) { 166 v, err := decodeBinHeader(rawDetails) 167 if err != nil { 168 return nil, err 169 } 170 st := &spb.Status{} 171 if err = proto.Unmarshal(v, st); err != nil { 172 return nil, err 173 } 174 return status.FromProto(st), nil 175 } 176 177 type timeoutUnit uint8 178 179 const ( 180 hour timeoutUnit = 'H' 181 minute timeoutUnit = 'M' 182 second timeoutUnit = 'S' 183 millisecond timeoutUnit = 'm' 184 microsecond timeoutUnit = 'u' 185 nanosecond timeoutUnit = 'n' 186 ) 187 188 func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) { 189 switch u { 190 case hour: 191 return time.Hour, true 192 case minute: 193 return time.Minute, true 194 case second: 195 return time.Second, true 196 case millisecond: 197 return time.Millisecond, true 198 case microsecond: 199 return time.Microsecond, true 200 case nanosecond: 201 return time.Nanosecond, true 202 default: 203 } 204 return 205 } 206 207 func decodeTimeout(s string) (time.Duration, error) { 208 size := len(s) 209 if size < 2 { 210 return 0, fmt.Errorf("transport: timeout string is too short: %q", s) 211 } 212 if size > 9 { 213 // Spec allows for 8 digits plus the unit. 214 return 0, fmt.Errorf("transport: timeout string is too long: %q", s) 215 } 216 unit := timeoutUnit(s[size-1]) 217 d, ok := timeoutUnitToDuration(unit) 218 if !ok { 219 return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s) 220 } 221 t, err := strconv.ParseInt(s[:size-1], 10, 64) 222 if err != nil { 223 return 0, err 224 } 225 const maxHours = math.MaxInt64 / int64(time.Hour) 226 if d == time.Hour && t > maxHours { 227 // This timeout would overflow math.MaxInt64; clamp it. 228 return time.Duration(math.MaxInt64), nil 229 } 230 return d * time.Duration(t), nil 231 } 232 233 const ( 234 spaceByte = ' ' 235 tildeByte = '~' 236 percentByte = '%' 237 ) 238 239 // encodeGrpcMessage is used to encode status code in header field 240 // "grpc-message". It does percent encoding and also replaces invalid utf-8 241 // characters with Unicode replacement character. 242 // 243 // It checks to see if each individual byte in msg is an allowable byte, and 244 // then either percent encoding or passing it through. When percent encoding, 245 // the byte is converted into hexadecimal notation with a '%' prepended. 246 func encodeGrpcMessage(msg string) string { 247 if msg == "" { 248 return "" 249 } 250 lenMsg := len(msg) 251 for i := 0; i < lenMsg; i++ { 252 c := msg[i] 253 if !(c >= spaceByte && c <= tildeByte && c != percentByte) { 254 return encodeGrpcMessageUnchecked(msg) 255 } 256 } 257 return msg 258 } 259 260 func encodeGrpcMessageUnchecked(msg string) string { 261 var buf bytes.Buffer 262 for len(msg) > 0 { 263 r, size := utf8.DecodeRuneInString(msg) 264 for _, b := range []byte(string(r)) { 265 if size > 1 { 266 // If size > 1, r is not ascii. Always do percent encoding. 267 buf.WriteString(fmt.Sprintf("%%%02X", b)) 268 continue 269 } 270 271 // The for loop is necessary even if size == 1. r could be 272 // utf8.RuneError. 273 // 274 // fmt.Sprintf("%%%02X", utf8.RuneError) gives "%FFFD". 275 if b >= spaceByte && b <= tildeByte && b != percentByte { 276 buf.WriteByte(b) 277 } else { 278 buf.WriteString(fmt.Sprintf("%%%02X", b)) 279 } 280 } 281 msg = msg[size:] 282 } 283 return buf.String() 284 } 285 286 // decodeGrpcMessage decodes the msg encoded by encodeGrpcMessage. 287 func decodeGrpcMessage(msg string) string { 288 if msg == "" { 289 return "" 290 } 291 lenMsg := len(msg) 292 for i := 0; i < lenMsg; i++ { 293 if msg[i] == percentByte && i+2 < lenMsg { 294 return decodeGrpcMessageUnchecked(msg) 295 } 296 } 297 return msg 298 } 299 300 func decodeGrpcMessageUnchecked(msg string) string { 301 var buf bytes.Buffer 302 lenMsg := len(msg) 303 for i := 0; i < lenMsg; i++ { 304 c := msg[i] 305 if c == percentByte && i+2 < lenMsg { 306 parsed, err := strconv.ParseUint(msg[i+1:i+3], 16, 8) 307 if err != nil { 308 buf.WriteByte(c) 309 } else { 310 buf.WriteByte(byte(parsed)) 311 i += 2 312 } 313 } else { 314 buf.WriteByte(c) 315 } 316 } 317 return buf.String() 318 } 319 320 type bufWriter struct { 321 buf []byte 322 offset int 323 batchSize int 324 conn net.Conn 325 err error 326 327 onFlush func() 328 } 329 330 func newBufWriter(conn net.Conn, batchSize int) *bufWriter { 331 return &bufWriter{ 332 buf: make([]byte, batchSize*2), 333 batchSize: batchSize, 334 conn: conn, 335 } 336 } 337 338 func (w *bufWriter) Write(b []byte) (n int, err error) { 339 if w.err != nil { 340 return 0, w.err 341 } 342 if w.batchSize == 0 { // Buffer has been disabled. 343 return w.conn.Write(b) 344 } 345 for len(b) > 0 { 346 nn := copy(w.buf[w.offset:], b) 347 b = b[nn:] 348 w.offset += nn 349 n += nn 350 if w.offset >= w.batchSize { 351 err = w.Flush() 352 } 353 } 354 return n, err 355 } 356 357 func (w *bufWriter) Flush() error { 358 if w.err != nil { 359 return w.err 360 } 361 if w.offset == 0 { 362 return nil 363 } 364 if w.onFlush != nil { 365 w.onFlush() 366 } 367 _, w.err = w.conn.Write(w.buf[:w.offset]) 368 w.offset = 0 369 return w.err 370 } 371 372 type framer struct { 373 writer *bufWriter 374 fr *http2.Framer 375 } 376 377 func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, maxHeaderListSize uint32) *framer { 378 if writeBufferSize < 0 { 379 writeBufferSize = 0 380 } 381 var r io.Reader = conn 382 if readBufferSize > 0 { 383 r = bufio.NewReaderSize(r, readBufferSize) 384 } 385 w := newBufWriter(conn, writeBufferSize) 386 f := &framer{ 387 writer: w, 388 fr: http2.NewFramer(w, r), 389 } 390 f.fr.SetMaxReadFrameSize(http2MaxFrameLen) 391 // Opt-in to Frame reuse API on framer to reduce garbage. 392 // Frames aren't safe to read from after a subsequent call to ReadFrame. 393 f.fr.SetReuseFrames() 394 f.fr.MaxHeaderListSize = maxHeaderListSize 395 f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil) 396 return f 397 } 398 399 // parseDialTarget returns the network and address to pass to dialer. 400 func parseDialTarget(target string) (string, string) { 401 net := "tcp" 402 m1 := strings.Index(target, ":") 403 m2 := strings.Index(target, ":/") 404 // handle unix:addr which will fail with url.Parse 405 if m1 >= 0 && m2 < 0 { 406 if n := target[0:m1]; n == "unix" { 407 return n, target[m1+1:] 408 } 409 } 410 if m2 >= 0 { 411 t, err := url.Parse(target) 412 if err != nil { 413 return net, target 414 } 415 scheme := t.Scheme 416 addr := t.Path 417 if scheme == "unix" { 418 if addr == "" { 419 addr = t.Host 420 } 421 return scheme, addr 422 } 423 } 424 return net, target 425 }