server unittest for validating client request / new timeout cancellation handling (need refactoring)

This commit is contained in:
Benjamin Sergeant
2019-01-02 16:08:32 -08:00
parent 3e064ec63e
commit cb904416c3
10 changed files with 280 additions and 44 deletions

View File

@ -222,4 +222,30 @@ namespace ix
}
}
}
std::pair<bool, std::string> Socket::readLine(const CancellationRequest& isCancellationRequested)
{
// FIXME: N should be a parameter
// Read first line
const int N = 255;
char line[N+1];
int i;
for (i = 0; i < 2 || (i < N && line[i-2] != '\r' && line[i-1] != '\n'); ++i)
{
if (!readByte(line+i, isCancellationRequested))
{
return std::make_pair(false, std::string());
}
}
if (i == N)
{
return std::make_pair(false, std::string());
}
line[i] = 0;
return std::make_pair(true, std::string(line));
}
}

View File

@ -45,6 +45,7 @@ namespace ix
const CancellationRequest& isCancellationRequested);
bool writeBytes(const std::string& str,
const CancellationRequest& isCancellationRequested);
std::pair<bool, std::string> readLine(const CancellationRequest& isCancellationRequested);
int getErrno() const;
static bool init(); // Required on Windows to initialize WinSocket

View File

@ -219,19 +219,22 @@ namespace ix
}
auto status = webSocket->connectToSocket(fd);
if (!status.success)
if (status.success)
{
// Process incoming messages and execute callbacks
// until the connection is closed
webSocket->run();
}
else
{
std::stringstream ss;
ss << "WebSocketServer::handleConnection() error: "
<< status.http_status
<< " error: "
<< status.errorStr;
logError(ss.str());
return;
}
// Process incoming messages and execute callbacks
// until the connection is closed
webSocket->run();
// Remove this client from our client set
{
std::lock_guard<std::mutex> lock(_clientsMutex);

View File

@ -31,6 +31,7 @@ namespace ix
void setOnConnectionCallback(const OnConnectionCallback& callback);
void start();
void wait();
void stop();
std::pair<bool, std::string> listen();
@ -65,7 +66,6 @@ namespace ix
// Methods
void run();
void stop();
void handleConnection(int fd);
// Logging

View File

@ -254,9 +254,20 @@ namespace ix
_socket = std::make_shared<Socket>();
}
auto isCancellationRequested = [this]() -> bool
// FIXME: timeout should be configurable
auto start = std::chrono::system_clock::now();
auto timeout = std::chrono::seconds(10);
auto isCancellationRequested = [this, start, timeout]() -> bool
{
return _requestInitCancellation;
// Was an explicit cancellation requested ?
if (_requestInitCancellation) return true;
auto now = std::chrono::system_clock::now();
if ((now - start) > timeout) return true;
// No cancellation request
return false;
};
std::string errMsg;
@ -300,27 +311,22 @@ namespace ix
return WebSocketInitResult(false, 0, std::string("Failed sending GET request to ") + url);
}
// Read first line
char line[256];
int i;
for (i = 0; i < 2 || (i < 255 && line[i-2] != '\r' && line[i-1] != '\n'); ++i)
// Read HTTP status line
auto lineResult = _socket->readLine(isCancellationRequested);
auto lineValid = lineResult.first;
auto line = lineResult.second;
if (!lineValid)
{
if (!_socket->readByte(line+i, isCancellationRequested))
{
return WebSocketInitResult(false, 0, std::string("Failed reading HTTP status line from ") + url);
}
}
line[i] = 0;
if (i == 255)
{
return WebSocketInitResult(false, 0, std::string("Got bad status line connecting to ") + _url);
return WebSocketInitResult(false, 0,
std::string("Failed reading HTTP status line from ") + url);
}
// Validate status
int status;
// HTTP/1.0 is too old.
if (sscanf(line, "HTTP/1.0 %d", &status) == 1)
if (sscanf(line.c_str(), "HTTP/1.0 %d", &status) == 1)
{
std::stringstream ss;
ss << "Server version is HTTP/1.0. Rejecting connection to " << host
@ -330,7 +336,7 @@ namespace ix
}
// We want an 101 HTTP status
if (sscanf(line, "HTTP/1.1 %d", &status) != 1 || status != 101)
if (sscanf(line.c_str(), "HTTP/1.1 %d", &status) != 1 || status != 101)
{
std::stringstream ss;
ss << "Got bad status connecting to " << host
@ -380,6 +386,28 @@ namespace ix
return WebSocketInitResult(true, status, "", headers);
}
WebSocketInitResult WebSocketTransport::sendErrorResponse(int code, std::string reason)
{
std::stringstream ss;
ss << "HTTP/1.1 ";
ss << code;
ss << "\r\n";
ss << reason;
ss << "\r\n";
auto isCancellationRequested = [this]() -> bool
{
return _requestInitCancellation;
};
if (!_socket->writeBytes(ss.str(), isCancellationRequested))
{
return WebSocketInitResult(false, 500, "Failed sending response");
}
return WebSocketInitResult(false, code, reason);
}
// Server
WebSocketInitResult WebSocketTransport::connectToSocket(int fd)
{
@ -391,28 +419,28 @@ namespace ix
_socket.reset();
_socket = std::make_shared<Socket>(fd);
auto isCancellationRequested = [this]() -> bool
// FIXME: timeout should be configurable
auto start = std::chrono::system_clock::now();
auto timeout = std::chrono::seconds(3);
auto isCancellationRequested = [this, start, timeout]() -> bool
{
return _requestInitCancellation;
// Was an explicit cancellation requested ?
if (_requestInitCancellation) return true;
auto now = std::chrono::system_clock::now();
if ((now - start) > timeout) return true;
// No cancellation request
return false;
};
std::string remote = std::string("remote fd ") + std::to_string(fd);
// Read first line
char line[256];
int i;
for (i = 0; i < 2 || (i < 255 && line[i-2] != '\r' && line[i-1] != '\n'); ++i)
{
if (!_socket->readByte(line+i, isCancellationRequested))
{
return WebSocketInitResult(false, 0, std::string("Failed reading HTTP status line from ") + remote);
}
}
line[i] = 0;
if (i == 255)
{
return WebSocketInitResult(false, 0, std::string("Got bad status line connecting to ") + remote);
}
auto lineResult = _socket->readLine(isCancellationRequested);
auto lineValid = lineResult.first;
auto line = lineResult.second;
// FIXME: Validate line content (GET /)
@ -422,13 +450,12 @@ namespace ix
if (!headersValid)
{
return WebSocketInitResult(false, 401, "Error parsing HTTP headers");
return sendErrorResponse(400, "Error parsing HTTP headers");
}
if (headers.find("sec-websocket-key") == headers.end())
{
std::string errorMsg("Missing Sec-WebSocket-Key value");
return WebSocketInitResult(false, 401, errorMsg);
return sendErrorResponse(400, "Missing Sec-WebSocket-Key value");
}
char output[29] = {};

View File

@ -165,5 +165,7 @@ namespace ix
// Parse HTTP headers
std::pair<bool, WebSocketHttpHeaders> parseHttpHeaders(const CancellationRequest& isCancellationRequested);
WebSocketInitResult sendErrorResponse(int code, std::string reason);
};
}