Skip to content

Commit 6397261

Browse files
garethsbras0219-msft
authored andcommitted
Add support for retrieving HTTP version of a request in HTTP listener (microsoft#565)
* Grab HTTP protocol version from the underlying request message and expose in http_request * Use error_code overload to resolve microsoft#545 * Rename get_http_version and get_remote_address without 'get_' prefix for consistency with rest of class public interface * Add test case for http_version * Fix GCC "error: changes meaning of 'http_version'..." that should have been in commit:1ba5ebfd * Use struct for http_version instead of pair. * Restore get_remote_address to avoid breaking source compat
1 parent 7db3585 commit 6397261

File tree

5 files changed

+107
-5
lines changed

5 files changed

+107
-5
lines changed

Release/include/cpprest/http_msg.h

+43
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,34 @@ namespace client
4343
class http_client;
4444
}
4545

46+
/// <summary>
47+
/// Represents the HTTP protocol version of a message, as {major, minor}.
48+
/// </summary>
49+
struct http_version
50+
{
51+
uint8_t major;
52+
uint8_t minor;
53+
54+
inline bool operator==(const http_version& other) const { return major == other.major && minor == other.minor; }
55+
inline bool operator<(const http_version& other) const { return major < other.major || (major == other.major && minor < other.minor); }
56+
57+
inline bool operator!=(const http_version& other) const { return !(*this == other); }
58+
inline bool operator>=(const http_version& other) const { return !(*this < other); }
59+
inline bool operator>(const http_version& other) const { return !(*this < other || *this == other); }
60+
inline bool operator<=(const http_version& other) const { return *this < other || *this == other; }
61+
};
62+
63+
/// <summary>
64+
/// Predefined HTTP protocol versions.
65+
/// </summary>
66+
class http_versions
67+
{
68+
public:
69+
_ASYNCRTIMP static const http_version HTTP_0_9;
70+
_ASYNCRTIMP static const http_version HTTP_1_0;
71+
_ASYNCRTIMP static const http_version HTTP_1_1;
72+
};
73+
4674
/// <summary>
4775
/// Predefined method strings for the standard HTTP methods mentioned in the
4876
/// HTTP 1.1 specification.
@@ -715,6 +743,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena
715743

716744
_ASYNCRTIMP void set_request_uri(const uri&);
717745

746+
http::http_version http_version() const { return m_http_version; }
747+
718748
const utility::string_t& remote_address() const { return m_remote_address; }
719749

720750
const pplx::cancellation_token &cancellation_token() const { return m_cancellationToken; }
@@ -757,6 +787,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena
757787

758788
void _set_base_uri(const http::uri &base_uri) { m_base_uri = base_uri; }
759789

790+
void _set_http_version(const http::http_version &http_version) { m_http_version = http_version; }
791+
760792
void _set_remote_address(const utility::string_t &remote_address) { m_remote_address = remote_address; }
761793

762794
private:
@@ -783,6 +815,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena
783815

784816
pplx::task_completion_event<http_response> m_response;
785817

818+
http::http_version m_http_version;
819+
786820
utility::string_t m_remote_address;
787821
};
788822

@@ -875,10 +909,19 @@ class http_request
875909
/// </remarks>
876910
const http_headers &headers() const { return _m_impl->headers(); }
877911

912+
/// <summary>
913+
/// Returns the HTTP protocol version of this request message.
914+
/// </summary>
915+
/// <returns>The HTTP protocol version.</returns>
916+
http::http_version http_version() const { return _m_impl->http_version(); }
917+
878918
/// <summary>
879919
/// Returns a string representation of the remote IP address.
880920
/// </summary>
881921
/// <returns>The remote IP address.</returns>
922+
const utility::string_t& remote_address() const { return _m_impl->remote_address(); }
923+
924+
CASABLANCA_DEPRECATED("Use `remote_address()` instead.")
882925
const utility::string_t& get_remote_address() const { return _m_impl->remote_address(); }
883926

884927
/// <summary>

Release/src/http/common/http_msg.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,8 @@ details::_http_request::_http_request(http::method mtd)
998998
: m_method(std::move(mtd)),
999999
m_initiated_response(0),
10001000
m_server_context(),
1001-
m_cancellationToken(pplx::cancellation_token::none())
1001+
m_cancellationToken(pplx::cancellation_token::none()),
1002+
m_http_version(http::http_version{0, 0})
10021003
{
10031004
if(m_method.empty())
10041005
{
@@ -1009,10 +1010,15 @@ details::_http_request::_http_request(http::method mtd)
10091010
details::_http_request::_http_request(std::unique_ptr<http::details::_http_server_context> server_context)
10101011
: m_initiated_response(0),
10111012
m_server_context(std::move(server_context)),
1012-
m_cancellationToken(pplx::cancellation_token::none())
1013+
m_cancellationToken(pplx::cancellation_token::none()),
1014+
m_http_version(http::http_version{0, 0})
10131015
{
10141016
}
10151017

1018+
const http_version http_versions::HTTP_0_9 = { 0, 9 };
1019+
const http_version http_versions::HTTP_1_0 = { 1, 0 };
1020+
const http_version http_versions::HTTP_1_1 = { 1, 1 };
1021+
10161022
#define _METHODS
10171023
#define DAT(a,b) const method methods::a = b;
10181024
#include "cpprest/details/http_constants.dat"

Release/src/http/listener/http_server_asio.cpp

+20-2
Original file line numberDiff line numberDiff line change
@@ -657,14 +657,32 @@ will_deref_and_erase_t asio_server_connection::handle_http_line(const boost::sys
657657

658658
// Get the version
659659
std::string http_version = http_path_and_version.substr(http_path_and_version.size() - VersionPortionSize + 1, VersionPortionSize - 2);
660+
661+
auto m_request_impl = m_request._get_impl().get();
662+
web::http::http_version parsed_version = { 0, 0 };
663+
if (boost::starts_with(http_version, "HTTP/"))
664+
{
665+
std::istringstream version{ http_version.substr(5) };
666+
version >> parsed_version.major;
667+
char dot; version >> dot;
668+
version >> parsed_version.minor;
669+
670+
m_request_impl->_set_http_version(parsed_version);
671+
}
672+
660673
// if HTTP version is 1.0 then disable pipelining
661-
if (http_version == "HTTP/1.0")
674+
if (parsed_version == web::http::http_versions::HTTP_1_0)
662675
{
663676
m_close = true;
664677
}
665678

666679
// Get the remote IP address
667-
m_request._get_impl()->_set_remote_address(utility::conversions::to_string_t(m_socket->remote_endpoint().address().to_string()));
680+
boost::system::error_code socket_ec;
681+
auto endpoint = m_socket->remote_endpoint(socket_ec);
682+
if (!socket_ec)
683+
{
684+
m_request._get_impl()->_set_remote_address(utility::conversions::to_string_t(endpoint.address().to_string()));
685+
}
668686

669687
return handle_headers();
670688
}

Release/src/http/listener/http_server_httpsys.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,8 @@ void windows_request_context::read_headers_io_completion(DWORD error_code, DWORD
560560
m_msg.set_method(parse_request_method(m_request));
561561
parse_http_headers(m_request->Headers, m_msg.headers());
562562

563+
m_msg._get_impl()->_set_http_version({ (uint8_t)m_request->Version.MajorVersion, (uint8_t)m_request->Version.MinorVersion });
564+
563565
// Retrieve the remote IP address
564566
std::vector<wchar_t> remoteAddressBuffer(50);
565567

Release/tests/functional/http/listener/request_handler_tests.cpp

+34-1
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,39 @@ TEST_FIXTURE(uri_address, test_leaks)
448448
listener.close().wait();
449449
}
450450

451+
TEST_FIXTURE(uri_address, http_version)
452+
{
453+
http_listener listener(U("http://localhost:45678/path1"));
454+
listener.open().wait();
455+
456+
test_http_client::scoped_client client(U("http://localhost:45678"));
457+
test_http_client * p_client = client.client();
458+
459+
volatile unsigned long requestCount = 0;
460+
461+
listener.support(methods::GET, [&requestCount](http_request request)
462+
{
463+
const auto& httpVersion = request.http_version();
464+
465+
// All clients currently use HTTP/1.1
466+
VERIFY_IS_TRUE(httpVersion == http_versions::HTTP_1_1);
467+
468+
os_utilities::interlocked_increment(&requestCount);
469+
request.reply(status_codes::NoContent);
470+
});
471+
472+
// Send a request to the listener
473+
VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1")));
474+
475+
p_client->next_response().then([](test_response *p_response)
476+
{
477+
http_asserts::assert_test_response_equals(p_response, status_codes::NoContent);
478+
}).wait();
479+
480+
VERIFY_IS_TRUE(requestCount >= 1);
481+
listener.close().wait();
482+
}
483+
451484
TEST_FIXTURE(uri_address, remote_address)
452485
{
453486
http_listener listener(U("http://localhost:45678/path1"));
@@ -460,7 +493,7 @@ TEST_FIXTURE(uri_address, remote_address)
460493

461494
listener.support(methods::GET, [&requestCount](http_request request)
462495
{
463-
const string_t& remoteAddr = request.get_remote_address();
496+
const string_t& remoteAddr = request.remote_address();
464497
const string_t& localhost4 = string_t(U("127.0.0.1"));
465498
const string_t& localhost6 = string_t(U("::1"));
466499

0 commit comments

Comments
 (0)