gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/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 "gitee.com/ks-custle/core-gm/gmhttp"
    36  
    37  	"gitee.com/ks-custle/core-gm/grpc/codes"
    38  	"gitee.com/ks-custle/core-gm/grpc/grpclog"
    39  	"gitee.com/ks-custle/core-gm/grpc/status"
    40  	"gitee.com/ks-custle/core-gm/net/http2"
    41  	"gitee.com/ks-custle/core-gm/net/http2/hpack"
    42  	"github.com/golang/protobuf/proto"
    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  }