fix close bug and tests : let poll do his job when closing (#79)

* let poll do his job when closing

* try fix test

* try fix test

* Update IXWebSocketTransport.cpp

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* add log to find issue on CI

* change state immediately, and send close frame after

* add immediate close test

* disable test for windows
This commit is contained in:
Kumamon38 2019-05-21 18:34:08 +02:00 committed by Benjamin Sergeant
parent d6eabae4f0
commit 8a4826164b
5 changed files with 107 additions and 38 deletions

View File

@ -293,8 +293,8 @@ namespace ix
break; break;
} }
// We cannot enter poll which might block forever if we are stopping // We can avoid to poll if we want to stop and are not closing
if (_stop) break; if (_stop && !isClosing()) break;
// 2. Poll to see if there's any new data available // 2. Poll to see if there's any new data available
WebSocketTransport::PollResult pollResult = _ws.poll(); WebSocketTransport::PollResult pollResult = _ws.poll();

View File

@ -71,7 +71,7 @@ namespace ix
const int WebSocketTransport::kDefaultPingIntervalSecs(-1); const int WebSocketTransport::kDefaultPingIntervalSecs(-1);
const int WebSocketTransport::kDefaultPingTimeoutSecs(-1); const int WebSocketTransport::kDefaultPingTimeoutSecs(-1);
const bool WebSocketTransport::kDefaultEnablePong(true); const bool WebSocketTransport::kDefaultEnablePong(true);
const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(200); const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(300);
constexpr size_t WebSocketTransport::kChunkSize; constexpr size_t WebSocketTransport::kChunkSize;
WebSocketTransport::WebSocketTransport() : WebSocketTransport::WebSocketTransport() :
@ -748,7 +748,7 @@ namespace ix
bool compress, bool compress,
const OnProgressCallback& onProgressCallback) const OnProgressCallback& onProgressCallback)
{ {
if (_readyState != ReadyState::OPEN) if (_readyState != ReadyState::OPEN && _readyState != ReadyState::CLOSING)
{ {
return WebSocketSendInfo(); return WebSocketSendInfo();
} }
@ -1042,7 +1042,9 @@ namespace ix
if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return; if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return;
sendCloseFrame(code, reason); // connection is opened, so close without sending close frame
if (_readyState == ReadyState::OPEN)
{
{ {
std::lock_guard<std::mutex> lock(_closeDataMutex); std::lock_guard<std::mutex> lock(_closeDataMutex);
_closeCode = code; _closeCode = code;
@ -1056,9 +1058,26 @@ namespace ix
} }
setReadyState(ReadyState::CLOSING); setReadyState(ReadyState::CLOSING);
sendCloseFrame(code, reason);
// wake up the poll, but do not close yet // wake up the poll, but do not close yet
_socket->wakeUpFromPoll(Socket::kSendRequest); _socket->wakeUpFromPoll(Socket::kSendRequest);
} }
else
{
{
std::lock_guard<std::mutex> lock(_closeDataMutex);
_closeCode = code;
_closeReason = reason;
_closeWireSize = closeWireSize;
_closeRemote = remote;
}
setReadyState(ReadyState::CLOSED);
// wake up the poll, and close
_socket->wakeUpFromPoll(Socket::kCloseRequest);
}
}
size_t WebSocketTransport::bufferedAmount() const size_t WebSocketTransport::bufferedAmount() const
{ {

View File

@ -44,13 +44,13 @@ if (UNIX)
list(APPEND SOURCES list(APPEND SOURCES
IXDNSLookupTest.cpp IXDNSLookupTest.cpp
cmd_websocket_chat.cpp cmd_websocket_chat.cpp
IXWebSocketCloseTest.cpp
) )
endif() endif()
# Disable tests for now that are failing or not reliable # Disable tests for now that are failing or not reliable
# IXWebSocketPingTest.cpp # IXWebSocketPingTest.cpp
# IXWebSocketPingTimeoutTest.cpp # IXWebSocketPingTimeoutTest.cpp
# IXWebSocketCloseTest.cpp
# IXWebSocketMessageQTest.cpp (trigger a segfault on Linux) # IXWebSocketMessageQTest.cpp (trigger a segfault on Linux)
add_executable(ixwebsocket_unittest ${SOURCES}) add_executable(ixwebsocket_unittest ${SOURCES})

View File

@ -102,6 +102,7 @@ namespace
} }
_webSocket.setUrl(url); _webSocket.setUrl(url);
_webSocket.disableAutomaticReconnection();
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + url); log(std::string("Connecting to url: ") + url);
@ -118,27 +119,27 @@ namespace
if (messageType == ix::WebSocketMessageType::Open) if (messageType == ix::WebSocketMessageType::Open)
{ {
log("client connected"); log("client connected");
_webSocket.disableAutomaticReconnection();
} }
else if (messageType == ix::WebSocketMessageType::Close) else if (messageType == ix::WebSocketMessageType::Close)
{ {
log("client disconnected"); std::stringstream ss;
ss << "client disconnected("
<< closeInfo.code
<< ","
<< closeInfo.reason
<< ")";
log(ss.str());
std::lock_guard<std::mutex> lck(_mutexCloseData); std::lock_guard<std::mutex> lck(_mutexCloseData);
_closeCode = closeInfo.code; _closeCode = closeInfo.code;
_closeReason = std::string(closeInfo.reason); _closeReason = std::string(closeInfo.reason);
_closeRemote = closeInfo.remote; _closeRemote = closeInfo.remote;
_webSocket.disableAutomaticReconnection();
} }
else if (messageType == ix::WebSocketMessageType::Error) else if (messageType == ix::WebSocketMessageType::Error)
{ {
ss << "Error ! " << error.reason; ss << "Error ! " << error.reason;
log(ss.str()); log(ss.str());
_webSocket.disableAutomaticReconnection();
} }
else if (messageType == ix::WebSocketMessageType::Pong) else if (messageType == ix::WebSocketMessageType::Pong)
{ {
@ -202,11 +203,13 @@ namespace
} }
else if (messageType == ix::WebSocketMessageType::Close) else if (messageType == ix::WebSocketMessageType::Close)
{ {
log("Server closed connection"); std::stringstream ss;
ss << "Server closed connection("
//Logger() << closeInfo.code; << closeInfo.code
//Logger() << closeInfo.reason; << ","
//Logger() << closeInfo.remote; << closeInfo.reason
<< ")";
log(ss.str());
std::lock_guard<std::mutex> lck(mutexWrite); std::lock_guard<std::mutex> lck(mutexWrite);
@ -261,11 +264,11 @@ TEST_CASE("Websocket_client_close_default", "[close]")
REQUIRE(server.getClients().size() == 1); REQUIRE(server.getClients().size() == 1);
ix::msleep(100); ix::msleep(500);
webSocketClient.stop(); webSocketClient.stop();
ix::msleep(200); ix::msleep(500);
// ensure client close is the same as values given // ensure client close is the same as values given
REQUIRE(webSocketClient.getCloseCode() == 1000); REQUIRE(webSocketClient.getCloseCode() == 1000);
@ -319,7 +322,7 @@ TEST_CASE("Websocket_client_close_params_given", "[close]")
REQUIRE(server.getClients().size() == 1); REQUIRE(server.getClients().size() == 1);
ix::msleep(100); ix::msleep(500);
webSocketClient.stop(4000, "My reason"); webSocketClient.stop(4000, "My reason");
@ -377,7 +380,7 @@ TEST_CASE("Websocket_server_close", "[close]")
REQUIRE(server.getClients().size() == 1); REQUIRE(server.getClients().size() == 1);
ix::msleep(200); ix::msleep(500);
server.stop(); server.stop();
@ -404,3 +407,50 @@ TEST_CASE("Websocket_server_close", "[close]")
ix::reportWebSocketTraffic(); ix::reportWebSocketTraffic();
} }
} }
TEST_CASE("Websocket_server_close_immediatly", "[close]")
{
SECTION("Make sure that close code and reason was read from server.")
{
ix::setupWebSocketTrafficTrackerCallback();
int port = getFreePort();
ix::WebSocketServer server(port);
uint16_t serverReceivedCloseCode(0);
bool serverReceivedCloseRemote(false);
std::string serverReceivedCloseReason("");
std::mutex mutexWrite;
REQUIRE(startServer(server, serverReceivedCloseCode, serverReceivedCloseReason, serverReceivedCloseRemote, mutexWrite));
std::string session = ix::generateSessionId();
WebSocketClient webSocketClient(port);
webSocketClient.start();
server.stop();
ix::msleep(500);
// ensure client close hasn't been called
REQUIRE(webSocketClient.getCloseCode() == 0);
REQUIRE(webSocketClient.getCloseReason() == "");
REQUIRE(webSocketClient.getCloseRemote() == false);
{
std::lock_guard<std::mutex> lck(mutexWrite);
// Here we ensure that the code/reason wasn't received by the server
REQUIRE(serverReceivedCloseCode == 0);
REQUIRE(serverReceivedCloseReason == "");
REQUIRE(serverReceivedCloseRemote == false);
}
// Give us 1000ms for the server to notice that clients went away
ix::msleep(1000);
REQUIRE(server.getClients().size() == 0);
ix::reportWebSocketTraffic();
}
}

View File

@ -336,8 +336,8 @@ TEST_CASE("Websocket_chat", "[websocket_chat]")
REQUIRE(chatA.getReceivedMessages()[1] == "from B2"); REQUIRE(chatA.getReceivedMessages()[1] == "from B2");
REQUIRE(chatA.getReceivedMessages()[2].size() == bigMessage.size()); REQUIRE(chatA.getReceivedMessages()[2].size() == bigMessage.size());
// Give us 500ms for the server to notice that clients went away // Give us 1000ms for the server to notice that clients went away
ix::msleep(500); ix::msleep(1000);
REQUIRE(server.getClients().size() == 0); REQUIRE(server.getClients().size() == 0);
ix::reportWebSocketTraffic(); ix::reportWebSocketTraffic();