Repository: valyala/fasthttp
Pull request #1767 (2024-04-23 12:51:28)

Implemented what was described by me in #1766.


Patch:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index c250bf7..06472d7 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -23,7 +23,7 @@ jobs:
         go-version: 1.20.x
     - run: go version
     - name: Run golangci-lint
-      uses: golangci/golangci-lint-action@v4
+      uses: golangci/golangci-lint-action@v5
       with:
         version: v1.56.2
         args: --verbose
diff --git a/client.go b/client.go
index 1f12d4a..5cae78d 100644
--- a/client.go
+++ b/client.go
@@ -2,6 +2,7 @@ package fasthttp
 
 import (
 	"bufio"
+	"bytes"
 	"crypto/tls"
 	"errors"
 	"fmt"
@@ -477,6 +478,10 @@ func (c *Client) Do(req *Request, resp *Response) error {
 
 	host := uri.Host()
 
+	if bytes.ContainsRune(host, ',') {
+		return fmt.Errorf("invalid host %q. Use HostClient for multiple hosts", host)
+	}
+
 	isTLS := false
 	if uri.isHTTPS() {
 		isTLS = true
diff --git a/fs.go b/fs.go
index 59638ad..9e15a0e 100644
--- a/fs.go
+++ b/fs.go
@@ -1233,6 +1233,12 @@ func (h *fsHandler) openIndexFile(ctx *RequestCtx, dirPath string, mustCompress
 		if err == nil {
 			return ff, nil
 		}
+		if mustCompress && err == errNoCreatePermission {
+			ctx.Logger().Printf("insufficient permissions for saving compressed file for %q. Serving uncompressed file. "+
+				"Allow write access to the directory with this file in order to improve fasthttp performance", indexFilePath)
+			mustCompress = false
+			return h.openFSFile(indexFilePath, mustCompress, fileEncoding)
+		}
 		if !errors.Is(err, fs.ErrNotExist) {
 			return nil, fmt.Errorf("cannot open file %q: %w", indexFilePath, err)
 		}
diff --git a/peripconn.go b/peripconn.go
index 123c55e..46bddbf 100644
--- a/peripconn.go
+++ b/peripconn.go
@@ -1,14 +1,16 @@
 package fasthttp
 
 import (
+	"crypto/tls"
 	"net"
 	"sync"
 )
 
 type perIPConnCounter struct {
-	pool sync.Pool
-	lock sync.Mutex
-	m    map[uint32]int
+	perIPConnPool    sync.Pool
+	perIPTLSConnPool sync.Pool
+	lock             sync.Mutex
+	m                map[uint32]int
 }
 
 func (cc *perIPConnCounter) Register(ip uint32) int {
@@ -43,8 +45,30 @@ type perIPConn struct {
 	perIPConnCounter *perIPConnCounter
 }
 
-func acquirePerIPConn(conn net.Conn, ip uint32, counter *perIPConnCounter) *perIPConn {
-	v := counter.pool.Get()
+type perIPTLSConn struct {
+	*tls.Conn
+
+	ip               uint32
+	perIPConnCounter *perIPConnCounter
+}
+
+func acquirePerIPConn(conn net.Conn, ip uint32, counter *perIPConnCounter) net.Conn {
+	if tlcConn, ok := conn.(*tls.Conn); ok {
+		v := counter.perIPTLSConnPool.Get()
+		if v == nil {
+			return &perIPTLSConn{
+				perIPConnCounter: counter,
+				Conn:             tlcConn,
+				ip:               ip,
+			}
+		}
+		c := v.(*perIPConn)
+		c.Conn = conn
+		c.ip = ip
+		return c
+	}
+
+	v := counter.perIPConnPool.Get()
 	if v == nil {
 		return &perIPConn{
 			perIPConnCounter: counter,
@@ -58,15 +82,19 @@ func acquirePerIPConn(conn net.Conn, ip uint32, counter *perIPConnCounter) *perI
 	return c
 }
 
-func releasePerIPConn(c *perIPConn) {
+func (c *perIPConn) Close() error {
+	err := c.Conn.Close()
+	c.perIPConnCounter.Unregister(c.ip)
 	c.Conn = nil
-	c.perIPConnCounter.pool.Put(c)
+	c.perIPConnCounter.perIPConnPool.Put(c)
+	return err
 }
 
-func (c *perIPConn) Close() error {
+func (c *perIPTLSConn) Close() error {
 	err := c.Conn.Close()
 	c.perIPConnCounter.Unregister(c.ip)
-	releasePerIPConn(c)
+	c.Conn = nil
+	c.perIPConnCounter.perIPTLSConnPool.Put(c)
 	return err
 }
 


Test patch:
diff --git a/peripconn_test.go b/peripconn_test.go
index 5571654..6bfccf1 100644
--- a/peripconn_test.go
+++ b/peripconn_test.go
@@ -4,6 +4,8 @@ import (
 	"testing"
 )
 
+var _ connTLSer = &perIPTLSConn{}
+
 func TestIPxUint32(t *testing.T) {
 	t.Parallel()