diff --git a/utils/consts.go b/utils/consts.go index 5891e363c..03e226fd1 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -724,7 +724,8 @@ const ( MetaSIPURIUser = "*sipuri_user" E164DomainConverter = "*e164Domain" E164Converter = "*e164" - UrlDecConverter = "*urldecode" + URLDecConverter = "*urldecode" + URLEncConverter = "*urlencode" MetaReload = "*reload" MetaLoad = "*load" MetaFloat64 = "*float64" diff --git a/utils/dataconverter.go b/utils/dataconverter.go index e2865a913..e01af861d 100644 --- a/utils/dataconverter.go +++ b/utils/dataconverter.go @@ -107,8 +107,10 @@ func NewDataConverter(params string) (conv DataConverter, err error) { return new(e164DomainConverter), nil case params == E164Converter: return new(e164Converter), nil - case params == UrlDecConverter: - return new(UrlDecodeConverter), nil + case params == URLDecConverter: + return new(URLDecodeConverter), nil + case params == URLEncConverter: + return new(URLEncodeConverter), nil case strings.HasPrefix(params, MetaLibPhoneNumber): if len(params) == len(MetaLibPhoneNumber) { return NewPhoneNumberConverter(EmptyString) @@ -756,10 +758,10 @@ func (sc StripConverter) Convert(in any) (any, error) { } } -// UrlDecodeConverter converts an URL with encoded special characters back to original string. -type UrlDecodeConverter struct{} +// URLDecodeConverter converts an URL with encoded special characters back to original string. +type URLDecodeConverter struct{} -func (UrlDecodeConverter) Convert(in any) (any, error) { +func (URLDecodeConverter) Convert(in any) (any, error) { urlStr := IfaceAsString(in) query, err := url.QueryUnescape(urlStr) if err != nil { @@ -767,3 +769,21 @@ func (UrlDecodeConverter) Convert(in any) (any, error) { } return query, nil } + +// URLEncodeConverter converts a string with special characters to a valid URL format. +type URLEncodeConverter struct{} + +func (URLEncodeConverter) Convert(in any) (any, error) { + urlStr := IfaceAsString(in) + parsedURL, err := url.Parse(urlStr) + if err != nil { + return nil, err + } + if parsedURL.Host == EmptyString { + return url.QueryEscape(parsedURL.Path), nil + } + if len(parsedURL.Query()) != 0 { + parsedURL.RawQuery = parsedURL.Query().Encode() + } + return parsedURL.String(), nil +} diff --git a/utils/dataconverter_test.go b/utils/dataconverter_test.go index aa53b46dd..9eceaf6cf 100644 --- a/utils/dataconverter_test.go +++ b/utils/dataconverter_test.go @@ -1644,7 +1644,7 @@ func TestStripConverter(t *testing.T) { } } -func TestURlConverter(t *testing.T) { +func TestURLDecodeConverter(t *testing.T) { tests := []struct { name string input string @@ -1669,8 +1669,14 @@ func TestURlConverter(t *testing.T) { expected: "query=@special#characters$", isError: false, }, + { + name: "Decode url with escaped chars", + input: "https://www.example.com/search?q=hello%20world&query=%40special%23characters%24&path=%2Fa%20b%2Fc%3Fd%3D1%26e%3D2&data=%E6%97%A5%E6%9C%AC%E8%AA%9E", + expected: "https://www.example.com/search?q=hello world&query=@special#characters$&path=/a b/c?d=1&e=2&data=日本語", + isError: false, + }, } - conv, err := NewDataConverter(UrlDecConverter) + conv, err := NewDataConverter(URLDecConverter) if err != nil { t.Fatal(err) } @@ -1692,3 +1698,31 @@ func TestURlConverter(t *testing.T) { }) } } + +func TestURLEncodeConverter(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + {name: "Encode a string with special character", input: "123$123", expected: "123%24123"}, + {name: "Encode special characters in path,query and fragment", input: "https://www.example.com/search日?data=日本語&path=/a b/c?d=1&e=2&q=hello world&query=@special#日本characters$", expected: "https://www.example.com/search%E6%97%A5?data=%E6%97%A5%E6%9C%AC%E8%AA%9E&e=2&path=%2Fa+b%2Fc%3Fd%3D1&q=hello+world&query=%40special#%E6%97%A5%E6%9C%ACcharacters$"}, + {name: "Encode a string with multiple special character", input: "foo☺@$'()*,baz;?&=#+!", expected: "foo%E2%98%BA%40%24%27%28%29%2A%2Cbaz%3B"}, + } + conv, err := NewDataConverter(URLEncConverter) + if err != nil { + t.Fatal(err) + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rcv, err := conv.Convert(tt.input) + if err != nil { + t.Error(err) + return + } + if tt.expected != rcv { + t.Errorf("expected %q,received %q", tt.expected, rcv) + } + }) + } +}