Repository: valyala/fasthttp
Pull request #2034 (2025-07-10 04:29:03)

chore(deps): bump golang.org/x/sys from 0.33.0 to 0.34.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.33.0 to 0.34.0.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/sys/commit/751c3c6ac2a644645976e8e7f3db0b75c87d32c6"><code>751c3c6</code></a> unix: add missing NFT_PAYLOAD_* consts on linux</li>
<li><a href="https://github.com/golang/sys/commit/0c740cc0f8b112e19e255caefb622a53779c0481"><code>0c740cc</code></a> unix: update Go to 1.24.3</li>
<li><a href="https://github.com/golang/sys/commit/d62d31c6166a69390ea553149bf921e215216610"><code>d62d31c</code></a> unix: update Linux constants and types to v6.14</li>
<li>See full diff in <a href="https://github.com/golang/sys/compare/v0.33.0...v0.34.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/sys&package-manager=go_modules&previous-version=0.33.0&new-version=0.34.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/go.mod b/go.mod
index a25f501..40ef335 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/valyala/bytebufferpool v1.0.0
 	golang.org/x/crypto v0.39.0
 	golang.org/x/net v0.41.0
-	golang.org/x/sys v0.33.0
+	golang.org/x/sys v0.34.0
 )
 
 require golang.org/x/text v0.26.0 // indirect
diff --git a/go.sum b/go.sum
index f1695a4..6a18a74 100644
--- a/go.sum
+++ b/go.sum
@@ -10,7 +10,7 @@ golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
 golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
 golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
 golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
-golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
-golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
+golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
 golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
diff --git a/header.go b/header.go
index f420a6c..0f262bc 100644
--- a/header.go
+++ b/header.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"io"
 	"iter"
+	"log/slog"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -3100,8 +3101,21 @@ type headerScanner struct {
 	nextNewLine int
 
 	initialized bool
+
+	// This is only used to print the deprecated newline separator warning.
+	// TODO: Remove this again once the newline separator is removed.
+	warned bool
 }
 
+// DeprecatedNewlineIncludeContext is used to control whether the context of the
+// header is included in the warning message about the deprecated newline
+// separator.
+// Warning: this can potentially leak sensitive information such as auth headers.
+var DeprecatedNewlineIncludeContext atomic.Bool
+
+// TODO: Remove this again once the newline separator is removed.
+var warnedAboutDeprecatedNewlineSeparatorLimiter atomic.Int64
+
 func (s *headerScanner) next() bool {
 	if !s.initialized {
 		s.nextColon = -1
@@ -3139,6 +3153,27 @@ func (s *headerScanner) next() bool {
 			s.err = errInvalidName
 			return false
 		}
+
+		// If the character before '\n' isn't '\r', print a warning.
+		if !s.warned && x > 1 && s.b[x-1] != rChar {
+			// Only warn once per second.
+			now := time.Now().Unix()
+			if warnedAboutDeprecatedNewlineSeparatorLimiter.Load() < now {
+				if warnedAboutDeprecatedNewlineSeparatorLimiter.Swap(now) < now {
+					if DeprecatedNewlineIncludeContext.Load() {
+						// Include 20 characters after the '\n'.
+						xx := x + 20
+						if len(s.b) < xx {
+							xx = len(s.b)
+						}
+						slog.Error("Deprecated newline only separator found in header", "context", fmt.Sprintf("%q", s.b[:xx]))
+					} else {
+						slog.Error("Deprecated newline only separator found in header")
+					}
+					s.warned = true
+				}
+			}
+		}
 	}
 	if n < 0 {
 		s.err = errNeedMore


Test patch:
diff --git a/header_test.go b/header_test.go
index 08eb2e2..b554432 100644
--- a/header_test.go
+++ b/header_test.go
@@ -3,10 +3,12 @@ package fasthttp
 import (
 	"bufio"
 	"bytes"
+	"context"
 	"encoding/base64"
 	"errors"
 	"fmt"
 	"io"
+	"log/slog"
 	"net/http"
 	"reflect"
 	"strconv"
@@ -14,6 +16,70 @@ import (
 	"testing"
 )
 
+type slogTestHandler struct {
+	out string
+}
+
+func (s *slogTestHandler) Enabled(_ context.Context, level slog.Level) bool {
+	return true
+}
+
+func (s *slogTestHandler) Handle(ctx context.Context, record slog.Record) error { //nolint:gocritic
+	s.out += record.Message
+	for r := range record.Attrs {
+		s.out += " " + r.Key + ":" + r.Value.String()
+	}
+	return nil
+}
+
+func (s *slogTestHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+	for _, attr := range attrs {
+		s.out += attr.String()
+	}
+	return &slogTestHandler{out: s.out}
+}
+
+func (s *slogTestHandler) WithGroup(name string) slog.Handler {
+	return &slogTestHandler{out: s.out}
+}
+
+func TestNewlineBackwardsCompatibleWarning(t *testing.T) {
+	h := &ResponseHeader{}
+
+	l := slog.Default()
+	ll := &slogTestHandler{}
+	slog.SetDefault(slog.New(ll))
+	defer slog.SetDefault(l)
+
+	DeprecatedNewlineIncludeContext.Store(true)
+	warnedAboutDeprecatedNewlineSeparatorLimiter.Store(0)
+
+	if err := h.Read(bufio.NewReader(bytes.NewBufferString("HTTP/1.1 200 OK\r\nContent-Type: foo/bar\nContent-Length: 12345\r\n\r\nsss"))); err != nil {
+		t.Fatal(err)
+	}
+	e := h.Peek(HeaderContentType)
+	if string(e) != "foo/bar" {
+		t.Fatalf("Unexpected Content-Type %q. Expected %q", e, "foo/bar")
+	}
+	expected := "Deprecated newline only separator found in header context:\"Content-Type: foo/bar\\nContent-Length: 123\""
+	if ll.out != expected {
+		t.Errorf("Expected %q, got %q", expected, ll.out)
+	}
+
+	ll.out = ""
+
+	DeprecatedNewlineIncludeContext.Store(false)
+	warnedAboutDeprecatedNewlineSeparatorLimiter.Store(0)
+
+	if err := h.Read(bufio.NewReader(bytes.NewBufferString("HTTP/1.1 200 OK\r\nContent-Type: foo/bar\nContent-Length: 12345\r\n\r\nsss"))); err != nil {
+		t.Fatal(err)
+	}
+	expected = "Deprecated newline only separator found in header"
+	if ll.out != expected {
+		t.Errorf("Expected %q, got %q", expected, ll.out)
+	}
+}
+
 func TestResponseHeaderAddContentType(t *testing.T) {
 	t.Parallel()