Repository: valyala/fasthttp
Pull request #1707 (2024-02-07 13:47:58)

Updates to GitHub Actions, add go1.22 to Test Matrix
- Add dependabot to track `github-actions` and `go` dependencies
- Add go1.22 to test matrix
- Update all outdated github actions

go 1.22 was released today: https://go.dev/blog/go1.22

gaby: @erikdubbelboer Quick PR to address some CI chores :-) 
gaby: The timeout in the tests are too agressive. If the runner is under heavily load it will cause the test to fail

Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..f34f8db
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "daily"
+  - package-ecosystem: "gomod"
+    directory: "/"
+    schedule:
+      interval: "daily"
\ No newline at end of file
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 5f5fa74..1d0603d 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -19,7 +19,7 @@ jobs:
         dry-run: false
         language: go
     - name: Upload Crash
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       if: failure() && steps.build.outcome == 'success'
       with:
         name: artifacts
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index cd41c03..1ca2ed1 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -8,8 +8,8 @@ jobs:
   lint:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
-    - uses: actions/setup-go@v4
+    - uses: actions/checkout@v4
+    - uses: actions/setup-go@v5
       with:
         go-version: 1.20.x
     - run: go version
diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml
index 5c592ad..31c0eda 100644
--- a/.github/workflows/security.yml
+++ b/.github/workflows/security.yml
@@ -14,7 +14,7 @@ jobs:
     env:
       GO111MODULE: on
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - name: Run Gosec Security Scanner
         uses: securego/gosec@v2.17.0
         with:
diff --git a/server.go b/server.go
index dbee2a4..e3593cd 100644
--- a/server.go
+++ b/server.go
@@ -431,6 +431,8 @@ type Server struct {
 	open int32
 	stop int32
 	done chan struct{}
+
+	rejectedRequestsCount uint32
 }
 
 // TimeoutHandler creates RequestHandler, which returns StatusRequestTimeout
@@ -1828,6 +1830,7 @@ func (s *Server) Serve(ln net.Listener) error {
 		atomic.AddInt32(&s.open, 1)
 		if !wp.Serve(c) {
 			atomic.AddInt32(&s.open, -1)
+			atomic.AddUint32(&s.rejectedRequestsCount, 1)
 			s.writeFastError(c, StatusServiceUnavailable,
 				"The connection cannot be served because Server.Concurrency limit exceeded")
 			c.Close()
@@ -2073,6 +2076,13 @@ func (s *Server) GetOpenConnectionsCount() int32 {
 	return atomic.LoadInt32(&s.open)
 }
 
+// GetRejectedConnectionsCount returns a number of rejected connections.
+//
+// This function is intended be used by monitoring systems.
+func (s *Server) GetRejectedConnectionsCount() uint32 {
+	return atomic.LoadUint32(&s.rejectedRequestsCount)
+}
+
 func (s *Server) getConcurrency() int {
 	n := s.Concurrency
 	if n <= 0 {
diff --git a/tcpdialer.go b/tcpdialer.go
index 7edd79f..e8430cb 100644
--- a/tcpdialer.go
+++ b/tcpdialer.go
@@ -151,6 +151,8 @@ type TCPDialer struct {
 	// }
 	Resolver Resolver
 
+	// DisableDNSResolution may be used to disable DNS resolution
+	DisableDNSResolution bool
 	// DNSCacheDuration may be used to override the default DNS cache duration (DefaultDNSCacheDuration)
 	DNSCacheDuration time.Duration
 
@@ -277,23 +279,26 @@ func (d *TCPDialer) dial(addr string, dualStack bool, timeout time.Duration) (ne
 			d.DNSCacheDuration = DefaultDNSCacheDuration
 		}
 
-		go d.tcpAddrsClean()
+		if !d.DisableDNSResolution {
+			go d.tcpAddrsClean()
+		}
 	})
-
 	deadline := time.Now().Add(timeout)
-	addrs, idx, err := d.getTCPAddrs(addr, dualStack, deadline)
-	if err != nil {
-		return nil, err
-	}
 	network := "tcp4"
 	if dualStack {
 		network = "tcp"
 	}
-
+	if d.DisableDNSResolution {
+		return d.tryDial(network, addr, deadline, d.concurrencyCh)
+	}
+	addrs, idx, err := d.getTCPAddrs(addr, dualStack, deadline)
+	if err != nil {
+		return nil, err
+	}
 	var conn net.Conn
 	n := uint32(len(addrs))
 	for n > 0 {
-		conn, err = d.tryDial(network, &addrs[idx%n], deadline, d.concurrencyCh)
+		conn, err = d.tryDial(network, addrs[idx%n].String(), deadline, d.concurrencyCh)
 		if err == nil {
 			return conn, nil
 		}
@@ -307,7 +312,7 @@ func (d *TCPDialer) dial(addr string, dualStack bool, timeout time.Duration) (ne
 }
 
 func (d *TCPDialer) tryDial(
-	network string, addr *net.TCPAddr, deadline time.Time, concurrencyCh chan struct{},
+	network string, addr string, deadline time.Time, concurrencyCh chan struct{},
 ) (net.Conn, error) {
 	timeout := time.Until(deadline)
 	if timeout <= 0 {
@@ -340,7 +345,7 @@ func (d *TCPDialer) tryDial(
 
 	ctx, cancelCtx := context.WithDeadline(context.Background(), deadline)
 	defer cancelCtx()
-	conn, err := dialer.DialContext(ctx, network, addr.String())
+	conn, err := dialer.DialContext(ctx, network, addr)
 	if err != nil && ctx.Err() == context.DeadlineExceeded {
 		return nil, ErrDialTimeout
 	}


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 8f5c3d8..5b0430d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,12 +9,12 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        go-version: [1.18.x, 1.19.x, 1.20.x, 1.21.x]
+        go-version: [1.18.x, 1.19.x, 1.20.x, 1.21.x, 1.22.x]
         os: [ubuntu-latest, macos-latest, windows-latest]
     runs-on: ${{ matrix.os }}
     steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-go@v4
+      - uses: actions/checkout@v4
+      - uses: actions/setup-go@v5
         with:
           go-version: ${{ matrix.go-version }}
 
diff --git a/server_test.go b/server_test.go
index efc126f..58454db 100644
--- a/server_test.go
+++ b/server_test.go
@@ -1016,6 +1016,62 @@ func TestServerConcurrencyLimit(t *testing.T) {
 	}
 }
 
+func TestRejectedRequestsCount(t *testing.T) {
+	t.Parallel()
+
+	s := &Server{
+		Handler: func(ctx *RequestCtx) {
+			ctx.WriteString("OK") //nolint:errcheck
+		},
+		Concurrency: 1,
+		Logger:      &testLogger{},
+	}
+
+	ln := fasthttputil.NewInmemoryListener()
+
+	serverCh := make(chan struct{})
+	go func() {
+		if err := s.Serve(ln); err != nil {
+			t.Errorf("unexpected error: %v", err)
+		}
+		close(serverCh)
+	}()
+
+	clientCh := make(chan struct{})
+	expectedCount := 5
+	go func() {
+		for i := 0; i < expectedCount+1; i++ {
+			_, err := ln.Dial()
+			if err != nil {
+				t.Errorf("unexpected error: %v", err)
+			}
+		}
+
+		if cnt := s.GetRejectedConnectionsCount(); cnt != uint32(expectedCount) {
+			t.Errorf("unexpected rejected connections count: %d. Expecting %d",
+				cnt, expectedCount)
+		}
+
+		close(clientCh)
+	}()
+
+	select {
+	case <-clientCh:
+	case <-time.After(time.Second):
+		t.Fatal("timeout")
+	}
+
+	if err := ln.Close(); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+
+	select {
+	case <-serverCh:
+	case <-time.After(time.Second):
+		t.Fatal("timeout")
+	}
+}
+
 func TestServerWriteFastError(t *testing.T) {
 	t.Parallel()