Repository: valyala/fasthttp
Pull request #1444 (2022-11-25 11:33:44)

client.go fix addMissingPort()
Currently it has a minor bug when the default port is not added for IPv6 addreses.
Likely in practice it doesn't affect the system.
Anyway please accept the PR that improves a performance a little bit and also exposes the addMissingPort() so that we can use it our apps code (now I had to copy the func).

stokito: @erikdubbelboer now the pr is ready to be merged. 
@li-jin-gou thank you for your contribution. 👍 

Patch:
diff --git a/client.go b/client.go
index c058f36..2b6b60f 100644
--- a/client.go
+++ b/client.go
@@ -9,7 +9,6 @@ import (
 	"fmt"
 	"io"
 	"net"
-	"strconv"
 	"strings"
 	"sync"
 	"sync/atomic"
@@ -495,7 +494,7 @@ func (c *Client) Do(req *Request, resp *Response) error {
 	hc := m[string(host)]
 	if hc == nil {
 		hc = &HostClient{
-			Addr:                          addMissingPort(string(host), isTLS),
+			Addr:                          AddMissingPort(string(host), isTLS),
 			Name:                          c.Name,
 			NoDefaultUserAgentHeader:      c.NoDefaultUserAgentHeader,
 			Dial:                          c.Dial,
@@ -1968,7 +1967,7 @@ func dialAddr(addr string, dial DialFunc, dialDualStack, isTLS bool, tlsConfig *
 		} else {
 			dial = Dial
 		}
-		addr = addMissingPort(addr, isTLS)
+		addr = AddMissingPort(addr, isTLS)
 	}
 	conn, err := dial(addr)
 	if err != nil {
@@ -2006,16 +2005,33 @@ func (c *HostClient) getClientName() []byte {
 	return clientName
 }
 
-func addMissingPort(addr string, isTLS bool) string {
-	n := strings.Index(addr, ":")
-	if n >= 0 {
+// AddMissingPort adds a port to a host if it is missing.
+// A literal IPv6 address in hostport must be enclosed in square
+// brackets, as in "[::1]:80", "[::1%lo0]:80".
+func AddMissingPort(addr string, isTLS bool) string {
+	addrLen := len(addr)
+	if addrLen == 0 {
 		return addr
 	}
-	port := 80
+
+	isIp6 := addr[0] == '['
+	if isIp6 {
+		// if the IPv6 has opening bracket but closing bracket is the last char then it doesn't have a port
+		isIp6WithoutPort := addr[addrLen-1] == ']'
+		if !isIp6WithoutPort {
+			return addr
+		}
+	} else { // IPv4
+		columnPos := strings.LastIndexByte(addr, ':')
+		if columnPos > 0 {
+			return addr
+		}
+	}
+	port := ":80"
 	if isTLS {
-		port = 443
+		port = ":443"
 	}
-	return net.JoinHostPort(addr, strconv.Itoa(port))
+	return addr + port
 }
 
 // A wantConn records state about a wanted connection


Test patch:
diff --git a/client_test.go b/client_test.go
index 13da521..b5c0b7f 100644
--- a/client_test.go
+++ b/client_test.go
@@ -2971,3 +2971,63 @@ func TestHttpsRequestWithoutParsedURL(t *testing.T) {
 		t.Fatal("https requests with IsTLS client must succeed")
 	}
 }
+
+func Test_AddMissingPort(t *testing.T) {
+	type args struct {
+		addr  string
+		isTLS bool
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			args: args{"127.1", false}, // 127.1 is a short form of 127.0.0.1
+			want: "127.1:80",
+		},
+		{
+			args: args{"127.0.0.1", false},
+			want: "127.0.0.1:80",
+		},
+		{
+			args: args{"127.0.0.1", true},
+			want: "127.0.0.1:443",
+		},
+		{
+			args: args{"[::1]", false},
+			want: "[::1]:80",
+		},
+		{
+			args: args{"::1", false},
+			want: "::1", // keep as is
+		},
+		{
+			args: args{"[::1]", true},
+			want: "[::1]:443",
+		},
+		{
+			args: args{"127.0.0.1:8080", false},
+			want: "127.0.0.1:8080",
+		},
+		{
+			args: args{"127.0.0.1:8443", true},
+			want: "127.0.0.1:8443",
+		},
+		{
+			args: args{"[::1]:8080", false},
+			want: "[::1]:8080",
+		},
+		{
+			args: args{"[::1]:8443", true},
+			want: "[::1]:8443",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.want, func(t *testing.T) {
+			if got := AddMissingPort(tt.args.addr, tt.args.isTLS); got != tt.want {
+				t.Errorf("AddMissingPort() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}