// should be reading from an io.LimitReader or similar Reader to bound
// the size of responses.
func NewReader(r *bufio.Reader) *Reader {
- commonHeaderOnce.Do(initCommonHeader)
return &Reader{R: r}
}
// If s contains a space or invalid header field bytes, it is
// returned without modifications.
func CanonicalMIMEHeaderKey(s string) string {
- commonHeaderOnce.Do(initCommonHeader)
-
// Quick check for canonical encoding.
upper := true
for i := 0; i < len(s); i++ {
a[i] = c
upper = c == '-' // for next time
}
+ commonHeaderOnce.Do(initCommonHeader)
// The compiler recognizes m[string(byteSlice)] as a special
// case, so a copy of a's bytes into a new string does not
// happen in this map lookup:
"bufio"
"bytes"
"io"
+ "net"
"reflect"
"strings"
+ "sync"
"testing"
)
}
}
+func TestIssue46363(t *testing.T) {
+ // Regression test for data race reported in issue 46363:
+ // ReadMIMEHeader reads commonHeader before commonHeader has been initialized.
+ // Run this test with the race detector enabled to catch the reported data race.
+
+ // Reset commonHeaderOnce, so that commonHeader will have to be initialized
+ commonHeaderOnce = sync.Once{}
+ commonHeader = nil
+
+ // Test for data race by calling ReadMIMEHeader and CanonicalMIMEHeaderKey concurrently
+
+ // Send MIME header over net.Conn
+ r, w := net.Pipe()
+ go func() {
+ // ReadMIMEHeader calls canonicalMIMEHeaderKey, which reads from commonHeader
+ NewConn(r).ReadMIMEHeader()
+ }()
+ w.Write([]byte("A: 1\r\nB: 2\r\nC: 3\r\n\r\n"))
+
+ // CanonicalMIMEHeaderKey calls commonHeaderOnce.Do(initCommonHeader) which initializes commonHeader
+ CanonicalMIMEHeaderKey("a")
+
+ if commonHeader == nil {
+ t.Fatal("CanonicalMIMEHeaderKey should initialize commonHeader")
+ }
+}
+
var clientHeaders = strings.Replace(`Host: golang.org
Connection: keep-alive
Cache-Control: max-age=0