package runtime

import (
	
	
	
	
	
	
	
	
	
	

	
	
	
	
)

// MetadataHeaderPrefix is the http prefix that represents custom metadata
// parameters to or from a gRPC call.
const MetadataHeaderPrefix = "Grpc-Metadata-"

// MetadataPrefix is prepended to permanent HTTP header keys (as specified
// by the IANA) when added to the gRPC context.
const MetadataPrefix = "grpcgateway-"

// MetadataTrailerPrefix is prepended to gRPC metadata as it is converted to
// HTTP headers in a response handled by grpc-gateway
const MetadataTrailerPrefix = "Grpc-Trailer-"

const metadataGrpcTimeout = "Grpc-Timeout"
const metadataHeaderBinarySuffix = "-Bin"

const xForwardedFor = "X-Forwarded-For"
const xForwardedHost = "X-Forwarded-Host"

// DefaultContextTimeout is used for gRPC call context.WithTimeout whenever a Grpc-Timeout inbound
// header isn't present. If the value is 0 the sent `context` will not have a timeout.
var DefaultContextTimeout = 0 * time.Second

// malformedHTTPHeaders lists the headers that the gRPC server may reject outright as malformed.
// See https://github.com/grpc/grpc-go/pull/4803#issuecomment-986093310 for more context.
var malformedHTTPHeaders = map[string]struct{}{
	"connection": {},
}

type (
	rpcMethodKey       struct{}
	httpPathPatternKey struct{}
	httpPatternKey     struct{}

	AnnotateContextOption func(ctx context.Context) context.Context
)

func ( string) AnnotateContextOption {
	return func( context.Context) context.Context {
		return withHTTPPathPattern(, )
	}
}

func decodeBinHeader( string) ([]byte, error) {
	if len()%4 == 0 {
		// Input was padded, or padding was not necessary.
		return base64.StdEncoding.DecodeString()
	}
	return base64.RawStdEncoding.DecodeString()
}

/*
AnnotateContext adds context information such as metadata from the request.

At a minimum, the RemoteAddr is included in the fashion of "X-Forwarded-For",
except that the forwarded destination is not another HTTP service but rather
a gRPC service.
*/
func ( context.Context,  *ServeMux,  *http.Request,  string,  ...AnnotateContextOption) (context.Context, error) {
	, ,  := annotateContext(, , , , ...)
	if  != nil {
		return nil, 
	}
	if  == nil {
		return , nil
	}

	return metadata.NewOutgoingContext(, ), nil
}

// AnnotateIncomingContext adds context information such as metadata from the request.
// Attach metadata as incoming context.
func ( context.Context,  *ServeMux,  *http.Request,  string,  ...AnnotateContextOption) (context.Context, error) {
	, ,  := annotateContext(, , , , ...)
	if  != nil {
		return nil, 
	}
	if  == nil {
		return , nil
	}

	return metadata.NewIncomingContext(, ), nil
}

func isValidGRPCMetadataKey( string) bool {
	// Must be a valid gRPC "Header-Name" as defined here:
	//   https://github.com/grpc/grpc/blob/4b05dc88b724214d0c725c8e7442cbc7a61b1374/doc/PROTOCOL-HTTP2.md
	// This means 0-9 a-z _ - .
	// Only lowercase letters are valid in the wire protocol, but the client library will normalize
	// uppercase ASCII to lowercase, so uppercase ASCII is also acceptable.
	 := []byte() // gRPC validates strings on the byte level, not Unicode.
	for ,  := range  {
		 :=  >= 'a' &&  <= 'z'
		 :=  >= 'A' &&  <= 'Z'
		 :=  >= '0' &&  <= '9'
		 :=  == '.' ||  == '-' ||  == '_'
		if ! && ! && ! && ! {
			return false
		}
	}
	return true
}

func isValidGRPCMetadataTextValue( string) bool {
	// Must be a valid gRPC "ASCII-Value" as defined here:
	//   https://github.com/grpc/grpc/blob/4b05dc88b724214d0c725c8e7442cbc7a61b1374/doc/PROTOCOL-HTTP2.md
	// This means printable ASCII (including/plus spaces); 0x20 to 0x7E inclusive.
	 := []byte() // gRPC validates strings on the byte level, not Unicode.
	for ,  := range  {
		if  < 0x20 ||  > 0x7E {
			return false
		}
	}
	return true
}

func annotateContext( context.Context,  *ServeMux,  *http.Request,  string,  ...AnnotateContextOption) (context.Context, metadata.MD, error) {
	 = withRPCMethod(, )
	for ,  := range  {
		 = ()
	}
	 := DefaultContextTimeout
	if  := .Header.Get(metadataGrpcTimeout);  != "" {
		var  error
		,  = timeoutDecode()
		if  != nil {
			return nil, nil, status.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", )
		}
	}
	var  []string
	for ,  := range .Header {
		 = textproto.CanonicalMIMEHeaderKey()
		switch  {
		case xForwardedFor, xForwardedHost:
			// Handled separately below
			continue
		}

		for ,  := range  {
			// For backwards-compatibility, pass through 'authorization' header with no prefix.
			if  == "Authorization" {
				 = append(, "authorization", )
			}
			if ,  := .incomingHeaderMatcher();  {
				if !isValidGRPCMetadataKey() {
					grpclog.Errorf("HTTP header name %q is not valid as gRPC metadata key; skipping", )
					continue
				}
				// Handles "-bin" metadata in grpc, since grpc will do another base64
				// encode before sending to server, we need to decode it first.
				if strings.HasSuffix(, metadataHeaderBinarySuffix) {
					,  := decodeBinHeader()
					if  != nil {
						return nil, nil, status.Errorf(codes.InvalidArgument, "invalid binary header %s: %s", , )
					}

					 = string()
				} else if !isValidGRPCMetadataTextValue() {
					grpclog.Errorf("Value of HTTP header %q contains non-ASCII value (not valid as gRPC metadata): skipping", )
					continue
				}
				 = append(, , )
			}
		}
	}
	if  := .Header.Get(xForwardedHost);  != "" {
		 = append(, strings.ToLower(xForwardedHost), )
	} else if .Host != "" {
		 = append(, strings.ToLower(xForwardedHost), .Host)
	}

	 := .Header.Values(xForwardedFor)
	if  := .RemoteAddr;  != "" {
		if , ,  := net.SplitHostPort();  == nil {
			 = append(, )
		}
	}
	if len() > 0 {
		 = append(, strings.ToLower(xForwardedFor), strings.Join(, ", "))
	}

	if  != 0 {
		, _ = context.WithTimeout(, )
	}
	if len() == 0 {
		return , nil, nil
	}
	 := metadata.Pairs(...)
	for ,  := range .metadataAnnotators {
		 = metadata.Join(, (, ))
	}
	return , , nil
}

// ServerMetadata consists of metadata sent from gRPC server.
type ServerMetadata struct {
	HeaderMD  metadata.MD
	TrailerMD metadata.MD
}

type serverMetadataKey struct{}

// NewServerMetadataContext creates a new context with ServerMetadata
func ( context.Context,  ServerMetadata) context.Context {
	if  == nil {
		 = context.Background()
	}
	return context.WithValue(, serverMetadataKey{}, )
}

// ServerMetadataFromContext returns the ServerMetadata in ctx
func ( context.Context) ( ServerMetadata,  bool) {
	if  == nil {
		return , false
	}
	,  = .Value(serverMetadataKey{}).(ServerMetadata)
	return
}

// ServerTransportStream implements grpc.ServerTransportStream.
// It should only be used by the generated files to support grpc.SendHeader
// outside of gRPC server use.
type ServerTransportStream struct {
	mu      sync.Mutex
	header  metadata.MD
	trailer metadata.MD
}

// Method returns the method for the stream.
func ( *ServerTransportStream) () string {
	return ""
}

// Header returns the header metadata of the stream.
func ( *ServerTransportStream) () metadata.MD {
	.mu.Lock()
	defer .mu.Unlock()
	return .header.Copy()
}

// SetHeader sets the header metadata.
func ( *ServerTransportStream) ( metadata.MD) error {
	if .Len() == 0 {
		return nil
	}

	.mu.Lock()
	.header = metadata.Join(.header, )
	.mu.Unlock()
	return nil
}

// SendHeader sets the header metadata.
func ( *ServerTransportStream) ( metadata.MD) error {
	return .SetHeader()
}

// Trailer returns the cached trailer metadata.
func ( *ServerTransportStream) () metadata.MD {
	.mu.Lock()
	defer .mu.Unlock()
	return .trailer.Copy()
}

// SetTrailer sets the trailer metadata.
func ( *ServerTransportStream) ( metadata.MD) error {
	if .Len() == 0 {
		return nil
	}

	.mu.Lock()
	.trailer = metadata.Join(.trailer, )
	.mu.Unlock()
	return nil
}

func timeoutDecode( string) (time.Duration, error) {
	 := len()
	if  < 2 {
		return 0, fmt.Errorf("timeout string is too short: %q", )
	}
	,  := timeoutUnitToDuration([-1])
	if ! {
		return 0, fmt.Errorf("timeout unit is not recognized: %q", )
	}
	,  := strconv.ParseInt([:-1], 10, 64)
	if  != nil {
		return 0, 
	}
	return  * time.Duration(), nil
}

func timeoutUnitToDuration( uint8) ( time.Duration,  bool) {
	switch  {
	case 'H':
		return time.Hour, true
	case 'M':
		return time.Minute, true
	case 'S':
		return time.Second, true
	case 'm':
		return time.Millisecond, true
	case 'u':
		return time.Microsecond, true
	case 'n':
		return time.Nanosecond, true
	default:
		return
	}
}

// isPermanentHTTPHeader checks whether hdr belongs to the list of
// permanent request headers maintained by IANA.
// http://www.iana.org/assignments/message-headers/message-headers.xml
func isPermanentHTTPHeader( string) bool {
	switch  {
	case
		"Accept",
		"Accept-Charset",
		"Accept-Language",
		"Accept-Ranges",
		"Authorization",
		"Cache-Control",
		"Content-Type",
		"Cookie",
		"Date",
		"Expect",
		"From",
		"Host",
		"If-Match",
		"If-Modified-Since",
		"If-None-Match",
		"If-Schedule-Tag-Match",
		"If-Unmodified-Since",
		"Max-Forwards",
		"Origin",
		"Pragma",
		"Referer",
		"User-Agent",
		"Via",
		"Warning":
		return true
	}
	return false
}

// isMalformedHTTPHeader checks whether header belongs to the list of
// "malformed headers" and would be rejected by the gRPC server.
func isMalformedHTTPHeader( string) bool {
	,  := malformedHTTPHeaders[strings.ToLower()]
	return 
}

// RPCMethod returns the method string for the server context. The returned
// string is in the format of "/package.service/method".
func ( context.Context) (string, bool) {
	 := .Value(rpcMethodKey{})
	if  == nil {
		return "", false
	}
	,  := .(string)
	if ! {
		return "", false
	}
	return , true
}

func withRPCMethod( context.Context,  string) context.Context {
	return context.WithValue(, rpcMethodKey{}, )
}

// HTTPPathPattern returns the HTTP path pattern string relating to the HTTP handler, if one exists.
// The format of the returned string is defined by the google.api.http path template type.
func ( context.Context) (string, bool) {
	 := .Value(httpPathPatternKey{})
	if  == nil {
		return "", false
	}
	,  := .(string)
	if ! {
		return "", false
	}
	return , true
}

func withHTTPPathPattern( context.Context,  string) context.Context {
	return context.WithValue(, httpPathPatternKey{}, )
}

// HTTPPattern returns the HTTP path pattern struct relating to the HTTP handler, if one exists.
func ( context.Context) (Pattern, bool) {
	,  := .Value(httpPatternKey{}).(Pattern)
	return , 
}

func withHTTPPattern( context.Context,  Pattern) context.Context {
	return context.WithValue(, httpPatternKey{}, )
}