github.com/prysmaticlabs/prysm@v1.4.4/shared/gateway/param_handling.go (about)

     1  package gateway
     2  
     3  import (
     4  	"encoding/base64"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  
     9  	"github.com/gorilla/mux"
    10  	butil "github.com/prysmaticlabs/prysm/shared/bytesutil"
    11  	"github.com/wealdtech/go-bytesutil"
    12  )
    13  
    14  // HandleURLParameters processes URL parameters, allowing parameterized URLs to be safely and correctly proxied to grpc-gateway.
    15  func HandleURLParameters(url string, req *http.Request, literals []string) ErrorJson {
    16  	segments := strings.Split(url, "/")
    17  
    18  segmentsLoop:
    19  	for i, s := range segments {
    20  		// We only care about segments which are parameterized.
    21  		if isRequestParam(s) {
    22  			// Don't do anything with parameters which should be forwarded literally to gRPC.
    23  			for _, l := range literals {
    24  				if s == "{"+l+"}" {
    25  					continue segmentsLoop
    26  				}
    27  			}
    28  
    29  			routeVar := mux.Vars(req)[s[1:len(s)-1]]
    30  			bRouteVar := []byte(routeVar)
    31  			isHex, err := butil.IsHex(bRouteVar)
    32  			if err != nil {
    33  				return InternalServerErrorWithMessage(err, "could not process URL parameter")
    34  			}
    35  			if isHex {
    36  				bRouteVar, err = bytesutil.FromHexString(string(bRouteVar))
    37  				if err != nil {
    38  					return InternalServerErrorWithMessage(err, "could not process URL parameter")
    39  				}
    40  			}
    41  			// Converting hex to base64 may result in a value which malforms the URL.
    42  			// We use URLEncoding to safely escape such values.
    43  			base64RouteVar := base64.URLEncoding.EncodeToString(bRouteVar)
    44  
    45  			// Merge segments back into the full URL.
    46  			splitPath := strings.Split(req.URL.Path, "/")
    47  			splitPath[i] = base64RouteVar
    48  			req.URL.Path = strings.Join(splitPath, "/")
    49  		}
    50  	}
    51  	return nil
    52  }
    53  
    54  // HandleQueryParameters processes query parameters, allowing them to be safely and correctly proxied to grpc-gateway.
    55  func HandleQueryParameters(req *http.Request, params []QueryParam) ErrorJson {
    56  	queryParams := req.URL.Query()
    57  
    58  	normalizeQueryValues(queryParams)
    59  
    60  	for key, vals := range queryParams {
    61  		for _, p := range params {
    62  			if key == p.Name {
    63  				if p.Hex {
    64  					queryParams.Del(key)
    65  					for _, v := range vals {
    66  						b := []byte(v)
    67  						isHex, err := butil.IsHex(b)
    68  						if err != nil {
    69  							return InternalServerErrorWithMessage(err, "could not process query parameter")
    70  						}
    71  						if isHex {
    72  							b, err = bytesutil.FromHexString(v)
    73  							if err != nil {
    74  								return InternalServerErrorWithMessage(err, "could not process query parameter")
    75  							}
    76  						}
    77  						queryParams.Add(key, base64.URLEncoding.EncodeToString(b))
    78  					}
    79  				}
    80  				if p.Enum {
    81  					queryParams.Del(key)
    82  					for _, v := range vals {
    83  						// gRPC expects uppercase enum values.
    84  						queryParams.Add(key, strings.ToUpper(v))
    85  					}
    86  				}
    87  			}
    88  		}
    89  	}
    90  	req.URL.RawQuery = queryParams.Encode()
    91  	return nil
    92  }
    93  
    94  // isRequestParam verifies whether the passed string is a request parameter.
    95  // Request parameters are enclosed in { and }.
    96  func isRequestParam(s string) bool {
    97  	return len(s) > 2 && s[0] == '{' && s[len(s)-1] == '}'
    98  }
    99  
   100  func normalizeQueryValues(queryParams url.Values) {
   101  	// Replace comma-separated values with individual values.
   102  	for key, vals := range queryParams {
   103  		splitVals := make([]string, 0)
   104  		for _, v := range vals {
   105  			splitVals = append(splitVals, strings.Split(v, ",")...)
   106  		}
   107  		queryParams[key] = splitVals
   108  	}
   109  }