github.com/avenga/couper@v1.12.2/eval/lib/url.go (about)

     1  package lib
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/zclconf/go-cty/cty"
    10  	"github.com/zclconf/go-cty/cty/function"
    11  )
    12  
    13  var (
    14  	// https://datatracker.ietf.org/doc/html/rfc3986#page-50
    15  	regexParseURL   = regexp.MustCompile(`^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?`)
    16  	URLEncodeFunc   = newURLEncodeFunction()
    17  	RelativeURLFunc = newRelativeURLFunction()
    18  )
    19  
    20  func newURLEncodeFunction() function.Function {
    21  	return function.New(&function.Spec{
    22  		Params: []function.Parameter{{
    23  			Name: "s",
    24  			Type: cty.String,
    25  		}},
    26  		Type: function.StaticReturnType(cty.String),
    27  		Impl: func(args []cty.Value, _ cty.Type) (ret cty.Value, err error) {
    28  			first := args[0]
    29  			result := strings.Replace(url.QueryEscape(first.AsString()), "+", "%20", -1)
    30  			return cty.StringVal(string(result)), nil
    31  		},
    32  	})
    33  }
    34  
    35  func AbsoluteURL(urlRef string, origin *url.URL) (string, error) {
    36  	u, err := url.Parse(urlRef)
    37  	if err != nil {
    38  		return "", err
    39  	}
    40  
    41  	if !u.IsAbs() {
    42  		return origin.ResolveReference(u).String(), nil
    43  	}
    44  	return urlRef, nil
    45  }
    46  
    47  func newRelativeURLFunction() function.Function {
    48  	return function.New(&function.Spec{
    49  		Params: []function.Parameter{{
    50  			Name: "s",
    51  			Type: cty.String,
    52  		}},
    53  		Type: function.StaticReturnType(cty.String),
    54  		Impl: func(args []cty.Value, _ cty.Type) (ret cty.Value, err error) {
    55  			absURL := strings.TrimSpace(args[0].AsString())
    56  
    57  			if !strings.HasPrefix(absURL, "/") && !strings.HasPrefix(absURL, "http://") && !strings.HasPrefix(absURL, "https://") {
    58  				return cty.StringVal(""), fmt.Errorf("invalid url given: %q", absURL)
    59  			}
    60  
    61  			// Do not use the result of url.Parse() to preserve the # character in an empty fragment.
    62  			if _, err := url.Parse(absURL); err != nil {
    63  				return cty.StringVal(""), err
    64  			}
    65  
    66  			// The regexParseURL garanties the len of 10 in the result.
    67  			urlParts := regexParseURL.FindStringSubmatch(absURL)
    68  
    69  			// The path must begin w/ a slash.
    70  			if !strings.HasPrefix(urlParts[5], "/") {
    71  				urlParts[5] = "/" + urlParts[5]
    72  			}
    73  
    74  			return cty.StringVal(urlParts[5] + urlParts[6] + urlParts[8]), nil
    75  		},
    76  	})
    77  }