net/http: `readLoopPeekFailLocked: <nil>` from client when server sends response immediately
#31,259 opened on Apr 4, 2019
Repository metrics
- Stars
- (133,883 stars)
- PR merge metrics
- (No merged PRs in 30d)
Description
What version of Go are you using (go version)?
Does this issue reproduce with the latest release?
Yep. 1.14.1
What operating system and processor architecture are you using (go env)?
What did you do?
100% reproducible on Linux x64, and play.golang.org. The behavior is unpredictable and the code (see below) to reproduce should be executed few times to got the error and the log.
The error is:
Get http://127.0.0.1:9101/path: readLoopPeekFailLocked: <nil>
The log is:
transport.go:1829: Unsolicited response received on idle HTTP channel starting with "HTTP/1.1 502 Bad Gateway\r\n\r\n"; err=<nil>
What happens?
HTTP server sends response without reading request and without a delay. This approach is good for proxy servers where a client determined by his IP (i.e. using white-list). Thus, we haven't to read request, if the user is not white-listed.
Reproducing
(try few times to got error)
package main
import (
"fmt"
"log"
"net"
"net/http"
"net/url"
"sync"
"time"
)
func mustParseURL(uri string) *url.URL {
x, err := url.Parse(uri)
if err != nil {
panic(err)
}
return x
}
func main() {
l, err := net.Listen("tcp", "127.0.0.1:9100")
if err != nil {
log.Fatal(err)
}
go func(l net.Listener) {
for {
c, err := l.Accept()
if err != nil {
log.Fatal(err)
}
go func(c net.Conn) {
defer c.Close()
fmt.Fprint(c, "HTTP/1.1 502 Bad Gateway\r\n\r\n")
}(c)
}
}(l)
time.Sleep(100 * time.Millisecond)
var cl http.Client
cl.Transport = &http.Transport{
Proxy: http.ProxyURL(mustParseURL("http://127.0.0.1:9100/")),
MaxConnsPerHost: 100,
}
const N = 100
var wg sync.WaitGroup
wg.Add(N)
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
resp, err := cl.Get("http://127.0.0.1:9101/path")
if err != nil {
log.Print("error:", err)
return
}
if resp.StatusCode != 502 {
log.Println("status:", resp.StatusCode)
}
resp.Body.Close()
}()
}
wg.Wait()
}
Also, playing with the N constant I've got this error for N=10, N=100 and N=1000. For N=1000 this error occurs every time. For N=10 rarely. Seems, it depends on goroutines scheduling.
What did you expect to see?
Got this error every time or never.
What did you see instead?
It's unpredictable. That makes testing a bit harder.
Also
Probably related issues #15253, #15446