Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions docs/url.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,33 @@ The above returns a dict, containing URL object:
```yaml
scheme: 'http'
host: 'server.com:8080'
hostname: 'server.com'
port: '8080'
path: '/api'
query: 'list=false'
opaque: nil
fragment: 'anchor'
userinfo: 'admin:secret'
```

IPv6 addresses are also supported:
```
urlParse "http://[2001:db8::1]:8080/api"
```

Returns:
```yaml
scheme: 'http'
host: '[2001:db8::1]:8080'
hostname: '2001:db8::1'
port: '8080'
path: '/api'
query: ''
opaque: nil
fragment: ''
userinfo: ''
```

For more info, check https://golang.org/pkg/net/url/#URL

## urlJoin
Expand All @@ -31,3 +51,23 @@ The above returns the following string:
```
proto://host:80/path?query#fragment
```

Alternatively, you can use `hostname` and `port` instead of `host`:
```
urlJoin (dict "fragment" "fragment" "hostname" "host" "port" "80" "path" "/path" "query" "query" "scheme" "http")
```

This will also produce:
```
proto://host:80/path?query#fragment
```

IPv6 addresses are automatically detected and wrapped in square brackets when using `hostname` and `port`:
```
urlJoin (dict "hostname" "2001:db8::1" "port" "8080" "path" "/path" "scheme" "http")
```

This produces:
```
http://[2001:db8::1]:8080/path
```
22 changes: 21 additions & 1 deletion url.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sprig

import (
"fmt"
"net"
"net/url"
"reflect"
)
Expand All @@ -28,6 +29,7 @@ func urlParse(v string) map[string]interface{} {
dict["scheme"] = parsedURL.Scheme
dict["host"] = parsedURL.Host
dict["hostname"] = parsedURL.Hostname()
dict["port"] = parsedURL.Port()
dict["path"] = parsedURL.Path
dict["query"] = parsedURL.RawQuery
dict["opaque"] = parsedURL.Opaque
Expand All @@ -43,9 +45,27 @@ func urlParse(v string) map[string]interface{} {

// join given dict to URL string
func urlJoin(d map[string]interface{}) string {
host := dictGetOrEmpty(d, "host")
// If host is not provided, try to construct it from hostname and port
if host == "" {
hostname := dictGetOrEmpty(d, "hostname")
port := dictGetOrEmpty(d, "port")
if port != "" {
// Check if hostname is an IPv6 address
ip := net.ParseIP(hostname)
if ip != nil && ip.To4() == nil {
// IPv6 address needs to be wrapped in square brackets
host = "[" + hostname + "]:" + port
} else {
host = hostname + ":" + port
}
} else {
host = hostname
}
}
resURL := url.URL{
Scheme: dictGetOrEmpty(d, "scheme"),
Host: dictGetOrEmpty(d, "host"),
Host: host,
Path: dictGetOrEmpty(d, "path"),
RawQuery: dictGetOrEmpty(d, "query"),
Opaque: dictGetOrEmpty(d, "opaque"),
Expand Down
41 changes: 41 additions & 0 deletions url_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var urlTests = map[string]map[string]interface{}{
"fragment": "fragment",
"host": "host:80",
"hostname": "host",
"port": "80",
"opaque": "",
"path": "/path",
"query": "query",
Expand All @@ -21,6 +22,7 @@ var urlTests = map[string]map[string]interface{}{
"fragment": "",
"host": "host:80",
"hostname": "host",
"port": "80",
"opaque": "",
"path": "/path",
"query": "",
Expand All @@ -31,6 +33,7 @@ var urlTests = map[string]map[string]interface{}{
"fragment": "",
"host": "",
"hostname": "",
"port": "",
"opaque": "",
"path": "something",
"query": "",
Expand All @@ -41,6 +44,7 @@ var urlTests = map[string]map[string]interface{}{
"fragment": "",
"host": "host:80",
"hostname": "host",
"port": "80",
"opaque": "",
"path": "/path",
"query": "",
Expand All @@ -51,12 +55,46 @@ var urlTests = map[string]map[string]interface{}{
"fragment": "",
"host": "host:80",
"hostname": "host",
"port": "80",
"opaque": "",
"path": "/pa th",
"query": "key=val%20ue",
"scheme": "proto",
"userinfo": "",
},
"proto://[2001:db8::1]:8080/path": {
"fragment": "",
"host": "[2001:db8::1]:8080",
"hostname": "2001:db8::1",
"port": "8080",
"opaque": "",
"path": "/path",
"query": "",
"scheme": "proto",
"userinfo": "",
},
"proto://[::1]:8080/path?query#fragment": {
"fragment": "fragment",
"host": "[::1]:8080",
"hostname": "::1",
"port": "8080",
"opaque": "",
"path": "/path",
"query": "query",
"scheme": "proto",
"userinfo": "",
},
"proto://user@[2001:db8::1]:8080/path": {
"fragment": "",
"host": "[2001:db8::1]:8080",
"hostname": "2001:db8::1",
"port": "8080",
"opaque": "",
"path": "/path",
"query": "",
"scheme": "proto",
"userinfo": "user",
},
}

func TestUrlParse(t *testing.T) {
Expand All @@ -75,6 +113,9 @@ func TestUrlJoin(t *testing.T) {
tests := map[string]string{
`{{ urlJoin (dict "fragment" "fragment" "host" "host:80" "path" "/path" "query" "query" "scheme" "proto") }}`: "proto://host:80/path?query#fragment",
`{{ urlJoin (dict "fragment" "fragment" "host" "host:80" "path" "/path" "scheme" "proto" "userinfo" "ASDJKJSD") }}`: "proto://ASDJKJSD@host:80/path#fragment",
// Test IPv6 with hostname and port
`{{ urlJoin (dict "hostname" "2001:db8::1" "port" "8080" "path" "/path" "scheme" "proto") }}`: "proto://[2001:db8::1]:8080/path",
`{{ urlJoin (dict "hostname" "::1" "port" "8080" "path" "/path" "scheme" "proto") }}`: "proto://[::1]:8080/path",
}
for tpl, expected := range tests {
assert.NoError(t, runt(tpl, expected))
Expand Down