Skip to content

Fix a regression in parsing an HTTP-Version string #677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Release/include/cpprest/http_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ struct http_version
inline bool operator>=(const http_version& other) const { return !(*this < other); }
inline bool operator>(const http_version& other) const { return !(*this < other || *this == other); }
inline bool operator<=(const http_version& other) const { return *this < other || *this == other; }

/// <summary>
/// Creates <c>http_version</c> from an HTTP-Version string, "HTTP" "/" 1*DIGIT "." 1*DIGIT.
/// </summary>
/// <returns>Returns a <c>http_version</c> of {0, 0} if not successful.</returns>
static _ASYNCRTIMP http_version __cdecl from_string(const utility::string_t& http_version_string);

/// <summary>
/// Returns the string representation of the <c>http_version</c>.
/// </summary>
_ASYNCRTIMP std::string to_utf8string() const;
};

/// <summary>
Expand Down
31 changes: 30 additions & 1 deletion Release/src/http/common/http_msg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,35 @@ void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers

}

http_version __cdecl http_version::from_string(const utility::string_t& http_version_string)
{
utility::istringstream_t str(http_version_string);
str.imbue(std::locale::classic());

utility::string_t http; std::getline(str, http, _XPLATSTR('/'));
unsigned int major = 0; str >> major;
utility::char_t dot = _XPLATSTR('\0'); str >> dot;
unsigned int minor = 0; str >> minor;

// check no failure, fully consumed, and correct fixed text
if (!str.fail() && str.eof() && _XPLATSTR("HTTP") == http && _XPLATSTR('.') == dot)
{
return{ (uint8_t)major, (uint8_t)minor };
}
return{ 0, 0 };
}

std::string http_version::to_utf8string() const
{
std::string ret;
ret.reserve(8);
ret.append("HTTP/");
ret.append(std::to_string(static_cast<unsigned int>(major)));
ret.append(".");
ret.append(std::to_string(static_cast<unsigned int>(minor)));
return ret;
}

static const utility::char_t * stream_was_set_explicitly = _XPLATSTR("A stream was set on the message and extraction is not possible");
static const utility::char_t * unsupported_charset = _XPLATSTR("Charset must be iso-8859-1, utf-8, utf-16, utf-16le, or utf-16be to be extracted.");

Expand Down Expand Up @@ -1052,4 +1081,4 @@ const http_version http_versions::HTTP_1_1 = { 1, 1 };
#undef DAT
#endif
}} // namespace web::http

12 changes: 2 additions & 10 deletions Release/src/http/listener/http_server_asio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,16 +659,8 @@ will_deref_and_erase_t asio_server_connection::handle_http_line(const boost::sys
std::string http_version = http_path_and_version.substr(http_path_and_version.size() - VersionPortionSize + 1, VersionPortionSize - 2);

auto m_request_impl = m_request._get_impl().get();
web::http::http_version parsed_version = { 0, 0 };
if (boost::starts_with(http_version, "HTTP/"))
{
std::istringstream version{ http_version.substr(5) };
version >> parsed_version.major;
char dot; version >> dot;
version >> parsed_version.minor;

m_request_impl->_set_http_version(parsed_version);
}
web::http::http_version parsed_version = web::http::http_version::from_string(utility::conversions::to_string_t(http_version));
m_request_impl->_set_http_version(parsed_version);

// if HTTP version is 1.0 then disable pipelining
if (parsed_version == web::http::http_versions::HTTP_1_0)
Expand Down
23 changes: 23 additions & 0 deletions Release/tests/functional/http/listener/request_handler_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,29 @@ TEST_FIXTURE(uri_address, test_leaks)

TEST_FIXTURE(uri_address, http_version)
{
// formatting should succeed
VERIFY_IS_TRUE("HTTP/0.9" == http_versions::HTTP_0_9.to_utf8string());
VERIFY_IS_TRUE("HTTP/1.0" == http_versions::HTTP_1_0.to_utf8string());
VERIFY_IS_TRUE("HTTP/1.1" == http_versions::HTTP_1_1.to_utf8string());
VERIFY_IS_TRUE("HTTP/12.3" == (http_version{ 12, 3 }).to_utf8string());
// parsing should succeed
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/0.9")) == http_versions::HTTP_0_9);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/1.0")) == http_versions::HTTP_1_0);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/1.1")) == http_versions::HTTP_1_1);
VERIFY_IS_TRUE((http_version::from_string(U("HTTP/12.3")) == http_version{ 12, 3 }));
// parsing should fail
http_version unknown = { 0, 0 };
VERIFY_IS_TRUE(http_version::from_string(U("http/12.3")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/12.3foo")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/12.")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/12")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/.3")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP/")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("HTTP")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("foo")) == unknown);
VERIFY_IS_TRUE(http_version::from_string(U("")) == unknown);

http_listener listener(U("http://localhost:45678/path1"));
listener.open().wait();

Expand Down