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 }