package transport
import (
spb
)
const (
http2MaxFrameLen = 16384
http2InitHeaderTableSize = 4096
)
var (
clientPreface = []byte(http2.ClientPreface)
http2ErrConvTab = map[http2.ErrCode]codes.Code{
http2.ErrCodeNo: codes.Internal,
http2.ErrCodeProtocol: codes.Internal,
http2.ErrCodeInternal: codes.Internal,
http2.ErrCodeFlowControl: codes.ResourceExhausted,
http2.ErrCodeSettingsTimeout: codes.Internal,
http2.ErrCodeStreamClosed: codes.Internal,
http2.ErrCodeFrameSize: codes.Internal,
http2.ErrCodeRefusedStream: codes.Unavailable,
http2.ErrCodeCancel: codes.Canceled,
http2.ErrCodeCompression: codes.Internal,
http2.ErrCodeConnect: codes.Internal,
http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
http2.ErrCodeHTTP11Required: codes.Internal,
}
HTTPStatusConvTab = map[int]codes.Code{
http.StatusBadRequest: codes.Internal,
http.StatusUnauthorized: codes.Unauthenticated,
http.StatusForbidden: codes.PermissionDenied,
http.StatusNotFound: codes.Unimplemented,
http.StatusTooManyRequests: codes.Unavailable,
http.StatusBadGateway: codes.Unavailable,
http.StatusServiceUnavailable: codes.Unavailable,
http.StatusGatewayTimeout: codes.Unavailable,
}
logger = grpclog.Component("transport")
)
func ( string) bool {
if != "" && [0] == ':' {
return true
}
switch {
case "content-type",
"user-agent",
"grpc-message-type",
"grpc-encoding",
"grpc-message",
"grpc-status",
"grpc-timeout",
"grpc-status-details-bin",
"te":
return true
default:
return false
}
}
func ( string) bool {
switch {
case ":authority", "user-agent":
return true
default:
return false
}
}
const binHdrSuffix = "-bin"
func ( []byte) string {
return base64.RawStdEncoding.EncodeToString()
}
func ( string) ([]byte, error) {
if len()%4 == 0 {
return base64.StdEncoding.DecodeString()
}
return base64.RawStdEncoding.DecodeString()
}
func (, string) string {
if strings.HasSuffix(, binHdrSuffix) {
return encodeBinHeader(([]byte)())
}
return
}
func (, string) (string, error) {
if strings.HasSuffix(, binHdrSuffix) {
, := decodeBinHeader()
return string(),
}
return , nil
}
func ( string) (*status.Status, error) {
, := decodeBinHeader()
if != nil {
return nil,
}
:= &spb.Status{}
if = proto.Unmarshal(, ); != nil {
return nil,
}
return status.FromProto(), nil
}
type timeoutUnit uint8
const (
hour timeoutUnit = 'H'
minute timeoutUnit = 'M'
second timeoutUnit = 'S'
millisecond timeoutUnit = 'm'
microsecond timeoutUnit = 'u'
nanosecond timeoutUnit = 'n'
)
func ( timeoutUnit) ( time.Duration, bool) {
switch {
case hour:
return time.Hour, true
case minute:
return time.Minute, true
case second:
return time.Second, true
case millisecond:
return time.Millisecond, true
case microsecond:
return time.Microsecond, true
case nanosecond:
return time.Nanosecond, true
default:
}
return
}
func ( string) (time.Duration, error) {
:= len()
if < 2 {
return 0, fmt.Errorf("transport: timeout string is too short: %q", )
}
if > 9 {
return 0, fmt.Errorf("transport: timeout string is too long: %q", )
}
:= timeoutUnit([-1])
, := timeoutUnitToDuration()
if ! {
return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", )
}
, := strconv.ParseInt([:-1], 10, 64)
if != nil {
return 0,
}
const = math.MaxInt64 / int64(time.Hour)
if == time.Hour && > {
return time.Duration(math.MaxInt64), nil
}
return * time.Duration(), nil
}
const (
spaceByte = ' '
tildeByte = '~'
percentByte = '%'
)
func ( string) string {
if == "" {
return ""
}
:= len()
for := 0; < ; ++ {
:= []
if !( >= spaceByte && <= tildeByte && != percentByte) {
return encodeGrpcMessageUnchecked()
}
}
return
}
func ( string) string {
var strings.Builder
for len() > 0 {
, := utf8.DecodeRuneInString()
for , := range []byte(string()) {
if > 1 {
fmt.Fprintf(&, "%%%02X", )
continue
}
if >= spaceByte && <= tildeByte && != percentByte {
.WriteByte()
} else {
fmt.Fprintf(&, "%%%02X", )
}
}
= [:]
}
return .String()
}
func ( string) string {
if == "" {
return ""
}
:= len()
for := 0; < ; ++ {
if [] == percentByte && +2 < {
return decodeGrpcMessageUnchecked()
}
}
return
}
func ( string) string {
var strings.Builder
:= len()
for := 0; < ; ++ {
:= []
if == percentByte && +2 < {
, := strconv.ParseUint([+1:+3], 16, 8)
if != nil {
.WriteByte()
} else {
.WriteByte(byte())
+= 2
}
} else {
.WriteByte()
}
}
return .String()
}
type bufWriter struct {
buf []byte
offset int
batchSize int
conn net.Conn
err error
}
func ( net.Conn, int) *bufWriter {
return &bufWriter{
buf: make([]byte, *2),
batchSize: ,
conn: ,
}
}
func ( *bufWriter) ( []byte) ( int, error) {
if .err != nil {
return 0, .err
}
if .batchSize == 0 {
return .conn.Write()
}
for len() > 0 {
:= copy(.buf[.offset:], )
= [:]
.offset +=
+=
if .offset >= .batchSize {
= .Flush()
}
}
return ,
}
func ( *bufWriter) () error {
if .err != nil {
return .err
}
if .offset == 0 {
return nil
}
_, .err = .conn.Write(.buf[:.offset])
.offset = 0
return .err
}
type framer struct {
writer *bufWriter
fr *http2.Framer
}
func ( net.Conn, , int, uint32) *framer {
if < 0 {
= 0
}
var io.Reader =
if > 0 {
= bufio.NewReaderSize(, )
}
:= newBufWriter(, )
:= &framer{
writer: ,
fr: http2.NewFramer(, ),
}
.fr.SetMaxReadFrameSize(http2MaxFrameLen)
.fr.SetReuseFrames()
.fr.MaxHeaderListSize =
.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
return
}
func ( string) (string, string) {
:= "tcp"
:= strings.Index(, ":")
:= strings.Index(, ":/")
if >= 0 && < 0 {
if := [0:]; == "unix" {
return , [+1:]
}
}
if >= 0 {
, := url.Parse()
if != nil {
return ,
}
:= .Scheme
:= .Path
if == "unix" {
if == "" {
= .Host
}
return ,
}
}
return ,
}