diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ef766a..4c035c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Release 3.4.0 (unreleased) + +### Added + +- #472: Added `joinHostPort` and `splitHostPort` network functions + ## Release 3.3.0 (2024-08-29) ### Added diff --git a/docs/network.md b/docs/network.md index ea6cfd7f..65cbfd48 100644 --- a/docs/network.md +++ b/docs/network.md @@ -9,3 +9,24 @@ The `getHostByName` receives a domain name and returns the ip address. ``` getHostByName "www.google.com" would return the corresponding ip address of www.google.com ``` + +## joinHostPort + +`joinHostPort` combines a host and port into a network address of the form `host:port`. For IPv6 addresses the host is enclosed in square brackets: `[host]:port`. This is the inverse of `splitHostPort`. + +``` +joinHostPort "192.0.2.1" "80" -> "192.0.2.1:80" +joinHostPort "2001:db8::1" "80" -> "[2001:db8::1]:80" +joinHostPort "example.com" "443" -> "example.com:443" +``` + +## splitHostPort + +`splitHostPort` splits a network address of the form `host:port` or `[host]:port` into its host and port components, returning a map with keys `host` and `port`. This is the inverse of `joinHostPort`. + +``` +{{- with splitHostPort "192.0.2.1:80" }} +host: {{ .host }} +port: {{ .port }} +{{- end }} +``` diff --git a/functions.go b/functions.go index cda47d26..b99f832a 100644 --- a/functions.go +++ b/functions.go @@ -91,6 +91,8 @@ var nonhermeticFunctions = []string{ // Network "getHostByName", + "joinHostPort", + "splitHostPort", } var genericMap = map[string]interface{}{ @@ -272,6 +274,8 @@ var genericMap = map[string]interface{}{ // Network: "getHostByName": getHostByName, + "joinHostPort": joinHostPort, + "splitHostPort": splitHostPort, // Paths: "base": path.Base, diff --git a/network.go b/network.go index 108d78a9..5fe1eb31 100644 --- a/network.go +++ b/network.go @@ -10,3 +10,12 @@ func getHostByName(name string) string { //TODO: add error handing when release v3 comes out return addrs[rand.Intn(len(addrs))] } + +func joinHostPort(host, port string) string { + return net.JoinHostPort(host, port) +} + +func splitHostPort(hostport string) map[string]string { + host, port, _ := net.SplitHostPort(hostport) + return map[string]string{"host": host, "port": port} +} diff --git a/network_test.go b/network_test.go index 9c153f0a..91e5213c 100644 --- a/network_test.go +++ b/network_test.go @@ -16,3 +16,36 @@ func TestGetHostByName(t *testing.T) { assert.NotNil(t, ip) assert.NotEmpty(t, ip) } + +func TestJoinHostPort(t *testing.T) { + tests := []struct { + tpl string + expected string + }{ + {`{{joinHostPort "192.0.2.1" "80"}}`, "192.0.2.1:80"}, + {`{{joinHostPort "2001:db8::1" "80"}}`, "[2001:db8::1]:80"}, + {`{{joinHostPort "example.com" "443"}}`, "example.com:443"}, + } + for _, tt := range tests { + result, err := runRaw(tt.tpl, nil) + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } +} + +func TestSplitHostPort(t *testing.T) { + tests := []struct { + tpl string + expectedHost string + expectedPort string + }{ + {`{{with splitHostPort "192.0.2.1:80"}}{{.host}}/{{.port}}{{end}}`, "192.0.2.1", "80"}, + {`{{with splitHostPort "[2001:db8::1]:80"}}{{.host}}/{{.port}}{{end}}`, "2001:db8::1", "80"}, + {`{{with splitHostPort "example.com:443"}}{{.host}}/{{.port}}{{end}}`, "example.com", "443"}, + } + for _, tt := range tests { + result, err := runRaw(tt.tpl, nil) + assert.NoError(t, err) + assert.Equal(t, tt.expectedHost+"/"+tt.expectedPort, result) + } +}