Skip to content

Commit

Permalink
Support for WinHTTPAL curl-to-WinHTTP adapter (#1182)
Browse files Browse the repository at this point in the history
  • Loading branch information
franksinankaya authored and BillyONeal committed Sep 27, 2019
1 parent c4f37e7 commit f4c863b
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 49 deletions.
1 change: 1 addition & 0 deletions Release/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ endif()

include(cmake/cpprest_find_boost.cmake)
include(cmake/cpprest_find_zlib.cmake)
include(cmake/cpprest_find_winhttppal.cmake)
include(cmake/cpprest_find_openssl.cmake)
include(cmake/cpprest_find_websocketpp.cmake)
include(cmake/cpprest_find_brotli.cmake)
Expand Down
17 changes: 17 additions & 0 deletions Release/cmake/cpprest_find_winhttppal.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function(cpprest_find_winhttppal)
if(TARGET cpprestsdk_winhttppal_internal)
return()
endif()

if(NOT WINHTTPPAL_LIBRARY OR NOT WINHTTPPAL_INCLUDE_DIRS)
find_package(winhttppal REQUIRED)
endif()

add_library(cpprestsdk_winhttppal_internal INTERFACE)
if(TARGET winhttppal::winhttppal)
target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE winhttppal::winhttppal)
else()
target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$<BUILD_INTERFACE:${WINHTTPPAL_LIBRARY}>")
target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$<BUILD_INTERFACE:${WINHTTPPAL_INCLUDE_DIRS}>")
endif()
endfunction()
4 changes: 4 additions & 0 deletions Release/cmake/cpprestsdk-config.in.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ if(@CPPREST_USES_OPENSSL@)
find_dependency(OpenSSL)
endif()

if(@CPPREST_USES_WINHTTPPAL@)
find_dependency(WINHTTPPAL)
endif()

if(@CPPREST_USES_BOOST@ AND OFF)
if(UNIX)
find_dependency(Boost COMPONENTS random system thread filesystem chrono atomic date_time regex)
Expand Down
8 changes: 4 additions & 4 deletions Release/include/cpprest/http_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class http_client_config
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
, m_tlsext_sni_enabled(true)
#endif
#if defined(_WIN32) && !defined(__cplusplus_winrt)
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
, m_buffer_request(false)
#endif
{
Expand Down Expand Up @@ -262,7 +262,7 @@ class http_client_config
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
#endif

#if defined(_WIN32) && !defined(__cplusplus_winrt)
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
/// <summary>
/// Checks if request data buffering is turned on, the default is off.
/// </summary>
Expand Down Expand Up @@ -389,7 +389,7 @@ class http_client_config
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
bool m_tlsext_sni_enabled;
#endif
#if defined(_WIN32) && !defined(__cplusplus_winrt)
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
bool m_buffer_request;
#endif
};
Expand Down Expand Up @@ -716,7 +716,7 @@ class http_client

namespace details
{
#if defined(_WIN32)
#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
extern const utility::char_t* get_with_body_err_msg;
#endif

Expand Down
12 changes: 12 additions & 0 deletions Release/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ if(CPPREST_HTTP_CLIENT_IMPL STREQUAL "asio")
target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_ASIO)
target_sources(cpprest PRIVATE http/client/http_client_asio.cpp http/client/x509_cert_utilities.cpp)
target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal)
elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttppal")
cpprest_find_boost()
cpprest_find_openssl()
cpprest_find_winhttppal()
target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
target_sources(cpprest PRIVATE http/client/http_client_winhttp.cpp http/client/x509_cert_utilities.cpp)
target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal cpprestsdk_winhttppal_internal)
elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttp")
target_link_libraries(cpprest PRIVATE
httpapi.lib
Expand Down Expand Up @@ -237,6 +244,7 @@ if(CPPREST_INSTALL)
set(CPPREST_USES_ZLIB OFF)
set(CPPREST_USES_BROTLI OFF)
set(CPPREST_USES_OPENSSL OFF)
set(CPPREST_USES_WINHTTPPAL OFF)

set(CPPREST_TARGETS cpprest)
if(TARGET cpprestsdk_boost_internal)
Expand All @@ -255,6 +263,10 @@ if(CPPREST_INSTALL)
list(APPEND CPPREST_TARGETS cpprestsdk_openssl_internal)
set(CPPREST_USES_OPENSSL ON)
endif()
if(TARGET cpprestsdk_winhttppal_internal)
list(APPEND CPPREST_TARGETS cpprestsdk_winhttppal_internal)
set(CPPREST_USES_WINHTTPPAL ON)
endif()
if(TARGET cpprestsdk_websocketpp_internal)
list(APPEND CPPREST_TARGETS cpprestsdk_websocketpp_internal)
endif()
Expand Down
4 changes: 2 additions & 2 deletions Release/src/http/client/http_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ static void verify_uri(const uri& uri)

namespace details
{
#if defined(_WIN32)
extern const utility::char_t* get_with_body_err_msg =
#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
const utility::char_t* get_with_body_err_msg =
_XPLATSTR("A GET or HEAD request should not have an entity body.");
#endif

Expand Down
5 changes: 1 addition & 4 deletions Release/src/http/client/http_client_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,10 @@ namespace details
/// Serialize the http_headers into name:value pairs separated by a carriage return and line feed.
/// </summary>
utility::string_t flatten_http_headers(const http_headers& headers);
#if defined(_WIN32)
/// <summary>
/// Parses a string containing Http headers.
/// </summary>
void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers);
#endif

void parse_headers_string(_Inout_z_ utility::char_t* headersStr, http_headers& headers);
} // namespace details
} // namespace http
} // namespace web
Expand Down
68 changes: 45 additions & 23 deletions Release/src/http/client/http_client_winhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@
#include "../common/internal_http_helpers.h"
#include "cpprest/http_headers.h"
#include "http_client_impl.h"
#ifdef WIN32
#include <Wincrypt.h>
#endif
#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
#include "winhttppal.h"
#endif
#include <atomic>

#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
#if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
#include <VersionHelpers.h>
#endif

Expand Down Expand Up @@ -97,7 +102,7 @@ static http::status_code parse_status_code(HINTERNET request_handle)
&buffer[0],
&length,
WINHTTP_NO_HEADER_INDEX);
return (unsigned short)_wtoi(buffer.c_str());
return (unsigned short)stoi(buffer);
}

// Helper function to get the reason phrase from a WinHTTP response.
Expand All @@ -122,7 +127,7 @@ static utility::string_t parse_reason_phrase(HINTERNET request_handle)
/// <summary>
/// Parses a string containing HTTP headers.
/// </summary>
static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utf16char* headersStr, http_response& response)
static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utility::char_t* headersStr, http_response& response)
{
// Clear the header map for each new response; otherwise, the header values will be combined.
response.headers().clear();
Expand All @@ -141,7 +146,7 @@ static std::string build_error_msg(unsigned long code, const std::string& locati
msg.append(": ");
msg.append(std::to_string(code));
msg.append(": ");
msg.append(utility::details::windows_category().message(code));
msg.append(utility::details::platform_category().message(static_cast<int>(code)));
return msg;
}

Expand All @@ -159,6 +164,7 @@ static std::string build_error_msg(_In_ WINHTTP_ASYNC_RESULT* error_result)
}
}


class memory_holder
{
uint8_t* m_externalData;
Expand Down Expand Up @@ -289,7 +295,7 @@ class winhttp_request_context final : public request_context
{
}

#if defined(_MSC_VER) && _MSC_VER < 1900
#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
compression_state(const compression_state&) = delete;
compression_state(compression_state&& other)
: m_buffer(std::move(other.m_buffer))
Expand Down Expand Up @@ -552,6 +558,10 @@ class winhttp_request_context final : public request_context

void on_send_request_validate_cn()
{
#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
// we do the validation inside curl
return;
#else
if (m_customCnCheck.empty())
{
// no custom validation selected; either we've delegated that to winhttp or
Expand Down Expand Up @@ -647,6 +657,7 @@ class winhttp_request_context final : public request_context
}

m_cachedEncodedCert.assign(encodedFirst, encodedLast);
#endif
}

protected:
Expand All @@ -666,13 +677,13 @@ class winhttp_request_context final : public request_context
winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request)
: request_context(client, request)
, m_request_handle(nullptr)
, m_bodyType(no_body)
, m_startingPosition(std::char_traits<uint8_t>::eof())
, m_body_data()
, m_remaining_to_write(0)
, m_proxy_authentication_tried(false)
, m_server_authentication_tried(false)
, m_bodyType(no_body)
, m_remaining_to_write(0)
, m_startingPosition(std::char_traits<uint8_t>::eof())
, m_readStream(request.body())
, m_body_data()
{
}
};
Expand Down Expand Up @@ -731,10 +742,10 @@ class winhttp_client final : public _http_client_communicator
public:
winhttp_client(http::uri address, http_client_config client_config)
: _http_client_communicator(std::move(address), std::move(client_config))
, m_secure(m_uri.scheme() == _XPLATSTR("https"))
, m_opened(false)
, m_hSession(nullptr)
, m_hConnection(nullptr)
, m_secure(m_uri.scheme() == _XPLATSTR("https"))
{
}

Expand All @@ -752,7 +763,7 @@ class winhttp_client final : public _http_client_communicator
if (m_hSession != nullptr)
{
// Unregister the callback.
WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL);
WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);

WinHttpCloseHandle(m_hSession);
}
Expand Down Expand Up @@ -792,8 +803,8 @@ class winhttp_client final : public _http_client_communicator
ie_proxy_config proxyIE;

DWORD access_type;
LPCWSTR proxy_name = WINHTTP_NO_PROXY_NAME;
LPCWSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS;
LPCTSTR proxy_name = WINHTTP_NO_PROXY_NAME;
LPCTSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS;
m_proxy_auto_config = false;
utility::string_t proxy_str;
http::uri uri;
Expand Down Expand Up @@ -901,7 +912,7 @@ class winhttp_client final : public _http_client_communicator
}

// Enable TLS 1.1 and 1.2
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
BOOL win32_result(FALSE);

DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
Expand Down Expand Up @@ -965,6 +976,17 @@ class winhttp_client final : public _http_client_communicator
proxy_info info;
bool proxy_info_required = false;

const auto& method = msg.method();

// stop injection of headers via method
// resource should be ok, since it's been encoded
// and host won't resolve
if (!::web::http::details::validate_method(method))
{
request->report_exception(http_exception("The method string is invalid."));
return;
}

if (m_proxy_auto_config)
{
WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {};
Expand Down Expand Up @@ -1416,7 +1438,6 @@ class winhttp_client final : public _http_client_communicator
{
return pplx::task_from_exception<size_t>(std::current_exception());
}
_ASSERTE(bytes_read >= 0);

uint8_t* buffer = p_request_context->m_compression_state.m_acquired;
if (buffer == nullptr)
Expand Down Expand Up @@ -1689,16 +1710,16 @@ class winhttp_client final : public _http_client_communicator
}
}

static std::wstring get_request_url(HINTERNET hRequestHandle)
static utility::string_t get_request_url(HINTERNET hRequestHandle)
{
std::wstring url;
utility::string_t url;
auto urlSize = static_cast<unsigned long>(url.capacity()) * 2; // use initial small string optimization capacity
for (;;)
{
url.resize(urlSize / sizeof(wchar_t));
if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], &urlSize))
url.resize(urlSize / sizeof(utility::char_t));
if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize))
{
url.resize(wcslen(url.c_str()));
url.resize(url.length());
return url;
}

Expand Down Expand Up @@ -2014,7 +2035,7 @@ class winhttp_client final : public _http_client_communicator
// Now allocate buffer for headers and query for them.
std::vector<unsigned char> header_raw_buffer;
header_raw_buffer.resize(headerBufferLength);
utf16char* header_buffer = reinterpret_cast<utf16char*>(&header_raw_buffer[0]);
utility::char_t* header_buffer = reinterpret_cast<utility::char_t*>(&header_raw_buffer[0]);
if (!WinHttpQueryHeaders(hRequestHandle,
WINHTTP_QUERY_RAW_HEADERS_CRLF,
WINHTTP_HEADER_NAME_BY_INDEX,
Expand Down Expand Up @@ -2052,7 +2073,7 @@ class winhttp_client final : public _http_client_communicator
!p_request_context->m_http_client->client_config().request_compressed_response())
{
p_request_context->m_compression_state.m_chunk =
std::make_unique<winhttp_request_context::compression_state::_chunk_helper>();
::utility::details::make_unique<winhttp_request_context::compression_state::_chunk_helper>();
p_request_context->m_compression_state.m_chunked = true;
}

Expand Down Expand Up @@ -2390,7 +2411,7 @@ class winhttp_client final : public _http_client_communicator
}).then([p_request_context](pplx::task<bool> op) {
try
{
bool ignored = op.get();
op.get();
}
catch (...)
{
Expand Down Expand Up @@ -2466,6 +2487,7 @@ std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(
return std::make_shared<details::winhttp_client>(std::move(base_uri), std::move(client_config));
}


} // namespace details
} // namespace client
} // namespace http
Expand Down
20 changes: 10 additions & 10 deletions Release/src/http/common/http_msg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,27 +225,27 @@ utility::string_t flatten_http_headers(const http_headers& headers)
return flattened_headers;
}

#if defined(_WIN32)
void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers)
void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers)
{
utf16char* context = nullptr;
utf16char* line = wcstok_s(headersStr, CRLF, &context);
while (line != nullptr)
utility::string_t str(headersStr);
std::size_t pos = str.find_first_of(_XPLATSTR("\r\n"));
std::size_t startpos = 0;
while (pos!=std::string::npos)
{
const utility::string_t header_line(line);
const utility::string_t header_line(str, startpos, pos - startpos);
const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":"));
if (colonIndex != utility::string_t::npos)
{
utility::string_t key = header_line.substr(0, colonIndex);
utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1);
http::details::trim_whitespace(key);
http::details::trim_whitespace(value);
web::http::details::trim_whitespace(key);
web::http::details::trim_whitespace(value);
headers.add(key, value);
}
line = wcstok_s(nullptr, CRLF, &context);
startpos = pos + 1;
pos = str.find_first_of(_XPLATSTR("\r\n"), pos + 1);
}
}
#endif

} // namespace details

Expand Down
Loading

0 comments on commit f4c863b

Please sign in to comment.