package spindle
import (
"io"
"net"
"net/http"
"testing"
"time"
"github.com/gorilla/websocket"
)
func TestHTTPServerDropsSlowClientsButNotWebsockets(t *testing.T) {
var upgrader websocket.Upgrader
mux := http.NewServeMux()
mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer conn.Close()
for {
typ, msg, err := conn.ReadMessage()
if err != nil || conn.WriteMessage(typ, msg) != nil {
return
}
}
})
srv := newHTTPServer("", mux)
if srv.ReadHeaderTimeout <= 0 || srv.ReadTimeout <= 0 || srv.IdleTimeout <= 0 {
t.Fatalf("timeouts header=%v read=%v idle=%v, want all set", srv.ReadHeaderTimeout, srv.ReadTimeout, srv.IdleTimeout)
}
// the real timeouts are too long to sit through in a test
srv.ReadHeaderTimeout = 200 * time.Millisecond
srv.ReadTimeout = 400 * time.Millisecond
srv.IdleTimeout = 400 * time.Millisecond
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
go srv.Serve(ln)
t.Cleanup(func() { srv.Close() })
slow, err := net.Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatal(err)
}
defer slow.Close()
if _, err := slow.Write([]byte("GET /ws HTTP/1.1\r\nHost: spindle\r\n")); err != nil {
t.Fatal(err)
}
slow.SetReadDeadline(time.Now().Add(3 * time.Second))
if _, err := io.ReadAll(slow); err != nil {
t.Fatalf("a connection that never finished its headers was still open after 3s: %v", err)
}
conn, _, err := websocket.DefaultDialer.Dial("ws://"+ln.Addr().String()+"/ws", nil)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
for _, pause := range []time.Duration{0, time.Second} {
time.Sleep(pause)
if err := conn.WriteMessage(websocket.TextMessage, []byte("ping")); err != nil {
t.Fatalf("websocket write after %v: %v", pause, err)
}
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
if _, _, err := conn.ReadMessage(); err != nil {
t.Fatalf("websocket echo after %v: %v, want it to outlive ReadTimeout", pause, err)
}
}
}