//go:build dev package webbundler import ( "bufio" "net" "net/http" "net/http/httptest" "strings" "testing" "time" ) // A real TCP client completes the WebSocket handshake against the hub (exercising // the http.Hijacker path a recorder can't), then receives a broadcast frame. This // covers the end-to-end push channel the browser HMR client relies on. func TestWebSocketHandshakeAndBroadcast(t *testing.T) { h := newHub() mux := http.NewServeMux() mux.HandleFunc("/@hmr/ws", h.ServeWS) srv := httptest.NewServer(mux) defer srv.Close() conn, err := net.Dial("tcp", strings.TrimPrefix(srv.URL, "http://")) if err != nil { t.Fatalf("dial: %v", err) } defer conn.Close() _, err = conn.Write([]byte( "GET /@hmr/ws HTTP/1.1\r\n" + "Host: " + srv.Listener.Addr().String() + "\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n\r\n", )) if err != nil { t.Fatalf("write handshake: %v", err) } br := bufio.NewReader(conn) status, err := br.ReadString('\n') if err != nil || !strings.Contains(status, "101") { t.Fatalf("expected 101 Switching Protocols, got %q (err %v)", status, err) } var acceptOK bool for { line, err := br.ReadString('\n') if err != nil { t.Fatalf("reading headers: %v", err) } if strings.HasPrefix(line, "Sec-WebSocket-Accept:") && strings.Contains(line, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") { acceptOK = true } if line == "\r\n" { break } } if !acceptOK { t.Fatal("missing/incorrect Sec-WebSocket-Accept header") } // The client must be registered before we broadcast. deadline := time.Now().Add(2 * time.Second) for h.clientCount() == 0 && time.Now().Before(deadline) { time.Sleep(5 * time.Millisecond) } if h.clientCount() != 1 { t.Fatalf("hub client count = %d, want 1", h.clientCount()) } h.broadcastJSON(hmrMessage{Type: "full-reload"}) conn.SetReadDeadline(time.Now().Add(2 * time.Second)) op, payload, err := readFrame(br) if err != nil { t.Fatalf("reading broadcast frame: %v", err) } if op != opText { t.Errorf("broadcast opcode = %#x, want text", op) } if !strings.Contains(string(payload), `"full-reload"`) { t.Errorf("broadcast payload = %q, want full-reload message", payload) } }