github.com/MetalBlockchain/metalgo@v1.11.9/vms/rpcchainvm/grpcutils/util.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package grpcutils
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"time"
    10  
    11  	"google.golang.org/grpc/status"
    12  	"google.golang.org/protobuf/proto"
    13  	"google.golang.org/protobuf/types/known/anypb"
    14  
    15  	httppb "github.com/MetalBlockchain/metalgo/proto/pb/http"
    16  	spb "google.golang.org/genproto/googleapis/rpc/status"
    17  	tspb "google.golang.org/protobuf/types/known/timestamppb"
    18  )
    19  
    20  func Errorf(code int, tmpl string, args ...interface{}) error {
    21  	return GetGRPCErrorFromHTTPResponse(&httppb.HandleSimpleHTTPResponse{
    22  		Code: int32(code),
    23  		Body: []byte(fmt.Sprintf(tmpl, args...)),
    24  	})
    25  }
    26  
    27  // GetGRPCErrorFromHTTPResponse takes an HandleSimpleHTTPResponse as input and returns a gRPC error.
    28  func GetGRPCErrorFromHTTPResponse(resp *httppb.HandleSimpleHTTPResponse) error {
    29  	a, err := anypb.New(resp)
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	return status.ErrorProto(&spb.Status{
    35  		Code:    resp.Code,
    36  		Message: string(resp.Body),
    37  		Details: []*anypb.Any{a},
    38  	})
    39  }
    40  
    41  // GetHTTPResponseFromError takes an gRPC error as input and returns a gRPC
    42  // HandleSimpleHTTPResponse.
    43  func GetHTTPResponseFromError(err error) (*httppb.HandleSimpleHTTPResponse, bool) {
    44  	s, ok := status.FromError(err)
    45  	if !ok {
    46  		return nil, false
    47  	}
    48  
    49  	status := s.Proto()
    50  	if len(status.Details) != 1 {
    51  		return nil, false
    52  	}
    53  
    54  	var resp httppb.HandleSimpleHTTPResponse
    55  	if err := anypb.UnmarshalTo(status.Details[0], &resp, proto.UnmarshalOptions{}); err != nil {
    56  		return nil, false
    57  	}
    58  
    59  	return &resp, true
    60  }
    61  
    62  // GetHTTPHeader takes an http.Header as input and returns a slice of Header.
    63  func GetHTTPHeader(hs http.Header) []*httppb.Element {
    64  	result := make([]*httppb.Element, 0, len(hs))
    65  	for k, vs := range hs {
    66  		result = append(result, &httppb.Element{
    67  			Key:    k,
    68  			Values: vs,
    69  		})
    70  	}
    71  	return result
    72  }
    73  
    74  // MergeHTTPHeader takes a slice of Header and merges with http.Header map.
    75  func MergeHTTPHeader(hs []*httppb.Element, header http.Header) {
    76  	for _, h := range hs {
    77  		header[h.Key] = h.Values
    78  	}
    79  }
    80  
    81  // TimestampAsTime validates timestamppb timestamp and returns time.Time.
    82  func TimestampAsTime(ts *tspb.Timestamp) (time.Time, error) {
    83  	if err := ts.CheckValid(); err != nil {
    84  		return time.Time{}, fmt.Errorf("invalid timestamp: %w", err)
    85  	}
    86  	return ts.AsTime(), nil
    87  }
    88  
    89  // TimestampFromTime converts time.Time to a timestamppb timestamp.
    90  func TimestampFromTime(time time.Time) *tspb.Timestamp {
    91  	return tspb.New(time)
    92  }
    93  
    94  // EnsureValidResponseCode ensures that the response code is valid otherwise it returns 500.
    95  func EnsureValidResponseCode(code int) int {
    96  	// Response code outside of this range is invalid and could panic.
    97  	// ref. https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
    98  	if code < 100 || code > 599 {
    99  		return http.StatusInternalServerError
   100  	}
   101  	return code
   102  }