improve some websocket error messages + add a utility function with unittest to parse status line and stop using scanf which triggers warnings on Windows

This commit is contained in:
Benjamin Sergeant 2019-09-09 21:23:57 -07:00
parent 6c0890594b
commit 64d3c99f99
6 changed files with 55 additions and 7 deletions

View File

@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
## [6.2.0] - 2019-09-09 ## [6.2.0] - 2019-09-09
- websocket and http server: server does not close the bound client socket in many cases - websocket and http server: server does not close the bound client socket in many cases
- improve some websocket error messages
- add a utility function with unittest to parse status line and stop using scanf which triggers warnings on Windows
## [6.1.0] - 2019-09-08 ## [6.1.0] - 2019-09-08

View File

@ -27,6 +27,36 @@ namespace ix
return out; return out;
} }
std::pair<std::string, int> Http::parseStatusLine(const std::string& line)
{
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
std::string token;
std::stringstream tokenStream(line);
std::vector<std::string> tokens;
// Split by ' '
while (std::getline(tokenStream, token, ' '))
{
tokens.push_back(token);
}
std::string httpVersion;
if (tokens.size() >= 1)
{
httpVersion = trim(tokens[0]);
}
int statusCode = -1;
if (tokens.size() >= 2)
{
std::stringstream ss;
ss << trim(tokens[1]);
ss >> statusCode;
}
return std::make_pair(httpVersion, statusCode);
}
std::tuple<std::string, std::string, std::string> Http::parseRequestLine(const std::string& line) std::tuple<std::string, std::string, std::string> Http::parseRequestLine(const std::string& line)
{ {
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF // Request-Line = Method SP Request-URI SP HTTP-Version CRLF

View File

@ -115,6 +115,8 @@ namespace ix
std::shared_ptr<Socket> socket); std::shared_ptr<Socket> socket);
static bool sendResponse(HttpResponsePtr response, std::shared_ptr<Socket> socket); static bool sendResponse(HttpResponsePtr response, std::shared_ptr<Socket> socket);
static std::pair<std::string, int> parseStatusLine(
const std::string& line);
static std::tuple<std::string, std::string, std::string> parseRequestLine( static std::tuple<std::string, std::string, std::string> parseRequestLine(
const std::string& line); const std::string& line);
static std::string trim(const std::string& str); static std::string trim(const std::string& str);

View File

@ -164,20 +164,23 @@ namespace ix
} }
// Validate status // Validate status
int status; auto statusLine = Http::parseStatusLine(line);
std::string httpVersion = statusLine.first;
int status = statusLine.second;
// HTTP/1.0 is too old. // HTTP/1.0 is too old.
if (sscanf(line.c_str(), "HTTP/1.0 %d", &status) == 1) if (httpVersion != "HTTP/1.1")
{ {
std::stringstream ss; std::stringstream ss;
ss << "Server version is HTTP/1.0. Rejecting connection to " << host ss << "Expecting HTTP/1.1, got " << httpVersion << ". "
<< "Rejecting connection to " << host
<< ", status: " << status << ", status: " << status
<< ", HTTP Status line: " << line; << ", HTTP Status line: " << line;
return WebSocketInitResult(false, status, ss.str()); return WebSocketInitResult(false, status, ss.str());
} }
// We want an 101 HTTP status // We want an 101 HTTP status
if (sscanf(line.c_str(), "HTTP/1.1 %d", &status) != 1 || status != 101) if (status != 101)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Got bad status connecting to " << host ss << "Got bad status connecting to " << host
@ -295,9 +298,15 @@ namespace ix
return sendErrorResponse(400, "Missing Sec-WebSocket-Key value"); return sendErrorResponse(400, "Missing Sec-WebSocket-Key value");
} }
if (headers.find("upgrade") == headers.end())
{
return sendErrorResponse(400, "Missing Upgrade header");
}
if (!insensitiveStringCompare(headers["upgrade"], "WebSocket")) if (!insensitiveStringCompare(headers["upgrade"], "WebSocket"))
{ {
return sendErrorResponse(400, "Invalid or missing Upgrade header"); return sendErrorResponse(400, "Invalid Upgrade header, "
"need WebSocket, got " + headers["upgrade"]);
} }
if (headers.find("sec-websocket-version") == headers.end()) if (headers.find("sec-websocket-version") == headers.end())
@ -314,7 +323,7 @@ namespace ix
if (version != 13) if (version != 13)
{ {
return sendErrorResponse(400, "Invalid Sec-WebSocket-Version, " return sendErrorResponse(400, "Invalid Sec-WebSocket-Version, "
"need 13, got" + ss.str()); "need 13, got " + ss.str());
} }
} }

View File

@ -681,8 +681,12 @@ namespace ix
reason = WebSocketCloseConstants::kInvalidFramePayloadDataMessage; reason = WebSocketCloseConstants::kInvalidFramePayloadDataMessage;
} }
//
// Validate close codes. Autobahn 7.9.* // Validate close codes. Autobahn 7.9.*
// 1014, 1015 are debattable. The firefox MSDN has a description for them // 1014, 1015 are debattable. The firefox MSDN has a description for them.
// Full list of status code and status range is defined in the dedicated
// RFC section at https://tools.ietf.org/html/rfc6455#page-45
//
if (code < 1000 || code == 1004 || code == 1006 || if (code < 1000 || code == 1004 || code == 1006 ||
(code > 1013 && code < 3000)) (code > 1013 && code < 3000))
{ {

View File

@ -62,6 +62,7 @@ set (SOURCES
IXHttpClientTest.cpp IXHttpClientTest.cpp
IXHttpServerTest.cpp IXHttpServerTest.cpp
IXUnityBuildsTest.cpp IXUnityBuildsTest.cpp
IXHttpTest.cpp
) )
# Some unittest don't work on windows yet # Some unittest don't work on windows yet