/*
 *
 * Copyright 2014 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package transport

import (
	
	
	
	
	
	
	
	
	
	
	
	

	
	
	
	spb 
	
	
	
)

const (
	// http2MaxFrameLen specifies the max length of a HTTP2 frame.
	http2MaxFrameLen = 16384 // 16KB frame
	// https://httpwg.org/specs/rfc7540.html#SettingValues
	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 is the HTTP status code to gRPC error code conversion table.
	HTTPStatusConvTab = map[int]codes.Code{
		// 400 Bad Request - INTERNAL.
		http.StatusBadRequest: codes.Internal,
		// 401 Unauthorized  - UNAUTHENTICATED.
		http.StatusUnauthorized: codes.Unauthenticated,
		// 403 Forbidden - PERMISSION_DENIED.
		http.StatusForbidden: codes.PermissionDenied,
		// 404 Not Found - UNIMPLEMENTED.
		http.StatusNotFound: codes.Unimplemented,
		// 429 Too Many Requests - UNAVAILABLE.
		http.StatusTooManyRequests: codes.Unavailable,
		// 502 Bad Gateway - UNAVAILABLE.
		http.StatusBadGateway: codes.Unavailable,
		// 503 Service Unavailable - UNAVAILABLE.
		http.StatusServiceUnavailable: codes.Unavailable,
		// 504 Gateway timeout - UNAVAILABLE.
		http.StatusGatewayTimeout: codes.Unavailable,
	}
	logger = grpclog.Component("transport")
)

// isReservedHeader checks whether hdr belongs to HTTP2 headers
// reserved by gRPC protocol. Any other headers are classified as the
// user-specified metadata.
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",
		// Intentionally exclude grpc-previous-rpc-attempts and
		// grpc-retry-pushback-ms, which are "reserved", but their API
		// intentionally works via metadata.
		"te":
		return true
	default:
		return false
	}
}

// isWhitelistedHeader checks whether hdr should be propagated into metadata
// visible to users, even though it is classified as "reserved", above.
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 {
		// Input was padded, or padding was not necessary.
		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 {
		// Spec allows for 8 digits plus the unit.
		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 &&  >  {
		// This timeout would overflow math.MaxInt64; clamp it.
		return time.Duration(math.MaxInt64), nil
	}
	return  * time.Duration(), nil
}

const (
	spaceByte   = ' '
	tildeByte   = '~'
	percentByte = '%'
)

// encodeGrpcMessage is used to encode status code in header field
// "grpc-message". It does percent encoding and also replaces invalid utf-8
// characters with Unicode replacement character.
//
// It checks to see if each individual byte in msg is an allowable byte, and
// then either percent encoding or passing it through. When percent encoding,
// the byte is converted into hexadecimal notation with a '%' prepended.
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 {
				// If size > 1, r is not ascii. Always do percent encoding.
				fmt.Fprintf(&, "%%%02X", )
				continue
			}

			// The for loop is necessary even if size == 1. r could be
			// utf8.RuneError.
			//
			// fmt.Sprintf("%%%02X", utf8.RuneError) gives "%FFFD".
			if  >= spaceByte &&  <= tildeByte &&  != percentByte {
				.WriteByte()
			} else {
				fmt.Fprintf(&, "%%%02X", )
			}
		}
		 = [:]
	}
	return .String()
}

// decodeGrpcMessage decodes the msg encoded by encodeGrpcMessage.
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 { // Buffer has been disabled.
		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)
	// Opt-in to Frame reuse API on framer to reduce garbage.
	// Frames aren't safe to read from after a subsequent call to ReadFrame.
	.fr.SetReuseFrames()
	.fr.MaxHeaderListSize = 
	.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
	return 
}

// parseDialTarget returns the network and address to pass to dialer.
func ( string) (string, string) {
	 := "tcp"
	 := strings.Index(, ":")
	 := strings.Index(, ":/")
	// handle unix:addr which will fail with url.Parse
	if  >= 0 &&  < 0 {
		if  := [0:];  == "unix" {
			return , [+1:]
		}
	}
	if  >= 0 {
		,  := url.Parse()
		if  != nil {
			return , 
		}
		 := .Scheme
		 := .Path
		if  == "unix" {
			if  == "" {
				 = .Host
			}
			return , 
		}
	}
	return , 
}