diff --git a/wpinet/src/main/native/cpp/WebSocket.cpp b/wpinet/src/main/native/cpp/WebSocket.cpp index dc2cd29837..0d9b66981a 100644 --- a/wpinet/src/main/native/cpp/WebSocket.cpp +++ b/wpinet/src/main/native/cpp/WebSocket.cpp @@ -573,6 +573,19 @@ void WebSocket::HandleIncoming(uv::Buffer& buf, size_t size) { if (!fin) { return Fail(1002, "cannot fragment control frames"); } + // If the connection is open, send a Pong in response + if (m_state == OPEN) { + SmallVector bufs; + { + raw_uv_ostream os{bufs, 4096}; + os << m_payload; + } + SendPong(bufs, [](auto bufs, uv::Error) { + for (auto&& buf : bufs) { + buf.Deallocate(); + } + }); + } ping(m_payload); break; case kOpPong: diff --git a/wpinet/src/main/native/include/wpinet/WebSocket.h b/wpinet/src/main/native/include/wpinet/WebSocket.h index 1f295c95de..6e886c3941 100644 --- a/wpinet/src/main/native/include/wpinet/WebSocket.h +++ b/wpinet/src/main/native/include/wpinet/WebSocket.h @@ -460,7 +460,8 @@ class WebSocket : public std::enable_shared_from_this { sig::Signal, bool> binary; /** - * Ping event. Emitted when a ping message is received. + * Ping event. Emitted when a ping message is received. A pong message is + * automatically sent in response, so this is simply a notification. */ sig::Signal> ping; diff --git a/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp b/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp index 5f6c8a54c9..cba37e3ed5 100644 --- a/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp +++ b/wpinet/src/test/native/cpp/WebSocketIntegrationTest.cpp @@ -147,4 +147,45 @@ TEST_F(WebSocketIntegrationTest, ClientSendText) { ASSERT_EQ(gotData, 1); } +TEST_F(WebSocketIntegrationTest, ServerSendPing) { + int gotPing = 0; + int gotPong = 0; + + serverPipe->Listen([&]() { + auto conn = serverPipe->Accept(); + auto server = WebSocketServer::Create(*conn); + server->connected.connect([&](std::string_view, WebSocket& ws) { + ws.SendPing({uv::Buffer{"\x03\x04", 2}}, [&](auto, uv::Error) {}); + ws.pong.connect([&](auto data) { + ++gotPong; + std::vector recvData{data.begin(), data.end()}; + std::vector expectData{0x03, 0x04}; + ASSERT_EQ(recvData, expectData); + ws.Close(); + }); + }); + }); + + clientPipe->Connect(pipeName, [&] { + auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName); + ws->closed.connect([&](uint16_t code, std::string_view reason) { + Finish(); + if (code != 1005 && code != 1006) { + FAIL() << "Code: " << code << " Reason: " << reason; + } + }); + ws->ping.connect([&](auto data) { + ++gotPing; + std::vector recvData{data.begin(), data.end()}; + std::vector expectData{0x03, 0x04}; + ASSERT_EQ(recvData, expectData); + }); + }); + + loop->Run(); + + ASSERT_EQ(gotPing, 1); + ASSERT_EQ(gotPong, 1); +} + } // namespace wpi