Skip to content

Commit f4c863b

Browse files
franksinankayaBillyONeal
authored andcommitted
Support for WinHTTPAL curl-to-WinHTTP adapter (microsoft#1182)
1 parent c4f37e7 commit f4c863b

File tree

13 files changed

+146
-49
lines changed

13 files changed

+146
-49
lines changed

Release/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ endif()
6161

6262
include(cmake/cpprest_find_boost.cmake)
6363
include(cmake/cpprest_find_zlib.cmake)
64+
include(cmake/cpprest_find_winhttppal.cmake)
6465
include(cmake/cpprest_find_openssl.cmake)
6566
include(cmake/cpprest_find_websocketpp.cmake)
6667
include(cmake/cpprest_find_brotli.cmake)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
function(cpprest_find_winhttppal)
2+
if(TARGET cpprestsdk_winhttppal_internal)
3+
return()
4+
endif()
5+
6+
if(NOT WINHTTPPAL_LIBRARY OR NOT WINHTTPPAL_INCLUDE_DIRS)
7+
find_package(winhttppal REQUIRED)
8+
endif()
9+
10+
add_library(cpprestsdk_winhttppal_internal INTERFACE)
11+
if(TARGET winhttppal::winhttppal)
12+
target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE winhttppal::winhttppal)
13+
else()
14+
target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$<BUILD_INTERFACE:${WINHTTPPAL_LIBRARY}>")
15+
target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$<BUILD_INTERFACE:${WINHTTPPAL_INCLUDE_DIRS}>")
16+
endif()
17+
endfunction()

Release/cmake/cpprestsdk-config.in.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ if(@CPPREST_USES_OPENSSL@)
1111
find_dependency(OpenSSL)
1212
endif()
1313

14+
if(@CPPREST_USES_WINHTTPPAL@)
15+
find_dependency(WINHTTPPAL)
16+
endif()
17+
1418
if(@CPPREST_USES_BOOST@ AND OFF)
1519
if(UNIX)
1620
find_dependency(Boost COMPONENTS random system thread filesystem chrono atomic date_time regex)

Release/include/cpprest/http_client.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class http_client_config
104104
#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO)
105105
, m_tlsext_sni_enabled(true)
106106
#endif
107-
#if defined(_WIN32) && !defined(__cplusplus_winrt)
107+
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
108108
, m_buffer_request(false)
109109
#endif
110110
{
@@ -262,7 +262,7 @@ class http_client_config
262262
void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; }
263263
#endif
264264

265-
#if defined(_WIN32) && !defined(__cplusplus_winrt)
265+
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
266266
/// <summary>
267267
/// Checks if request data buffering is turned on, the default is off.
268268
/// </summary>
@@ -389,7 +389,7 @@ class http_client_config
389389
std::function<void(boost::asio::ssl::context&)> m_ssl_context_callback;
390390
bool m_tlsext_sni_enabled;
391391
#endif
392-
#if defined(_WIN32) && !defined(__cplusplus_winrt)
392+
#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
393393
bool m_buffer_request;
394394
#endif
395395
};
@@ -716,7 +716,7 @@ class http_client
716716

717717
namespace details
718718
{
719-
#if defined(_WIN32)
719+
#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
720720
extern const utility::char_t* get_with_body_err_msg;
721721
#endif
722722

Release/src/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ if(CPPREST_HTTP_CLIENT_IMPL STREQUAL "asio")
131131
target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_ASIO)
132132
target_sources(cpprest PRIVATE http/client/http_client_asio.cpp http/client/x509_cert_utilities.cpp)
133133
target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal)
134+
elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttppal")
135+
cpprest_find_boost()
136+
cpprest_find_openssl()
137+
cpprest_find_winhttppal()
138+
target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
139+
target_sources(cpprest PRIVATE http/client/http_client_winhttp.cpp http/client/x509_cert_utilities.cpp)
140+
target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal cpprestsdk_winhttppal_internal)
134141
elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttp")
135142
target_link_libraries(cpprest PRIVATE
136143
httpapi.lib
@@ -237,6 +244,7 @@ if(CPPREST_INSTALL)
237244
set(CPPREST_USES_ZLIB OFF)
238245
set(CPPREST_USES_BROTLI OFF)
239246
set(CPPREST_USES_OPENSSL OFF)
247+
set(CPPREST_USES_WINHTTPPAL OFF)
240248

241249
set(CPPREST_TARGETS cpprest)
242250
if(TARGET cpprestsdk_boost_internal)
@@ -255,6 +263,10 @@ if(CPPREST_INSTALL)
255263
list(APPEND CPPREST_TARGETS cpprestsdk_openssl_internal)
256264
set(CPPREST_USES_OPENSSL ON)
257265
endif()
266+
if(TARGET cpprestsdk_winhttppal_internal)
267+
list(APPEND CPPREST_TARGETS cpprestsdk_winhttppal_internal)
268+
set(CPPREST_USES_WINHTTPPAL ON)
269+
endif()
258270
if(TARGET cpprestsdk_websocketpp_internal)
259271
list(APPEND CPPREST_TARGETS cpprestsdk_websocketpp_internal)
260272
endif()

Release/src/http/client/http_client.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ static void verify_uri(const uri& uri)
4141

4242
namespace details
4343
{
44-
#if defined(_WIN32)
45-
extern const utility::char_t* get_with_body_err_msg =
44+
#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
45+
const utility::char_t* get_with_body_err_msg =
4646
_XPLATSTR("A GET or HEAD request should not have an entity body.");
4747
#endif
4848

Release/src/http/client/http_client_impl.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,10 @@ namespace details
3030
/// Serialize the http_headers into name:value pairs separated by a carriage return and line feed.
3131
/// </summary>
3232
utility::string_t flatten_http_headers(const http_headers& headers);
33-
#if defined(_WIN32)
3433
/// <summary>
3534
/// Parses a string containing Http headers.
3635
/// </summary>
37-
void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers);
38-
#endif
39-
36+
void parse_headers_string(_Inout_z_ utility::char_t* headersStr, http_headers& headers);
4037
} // namespace details
4138
} // namespace http
4239
} // namespace web

Release/src/http/client/http_client_winhttp.cpp

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@
1818
#include "../common/internal_http_helpers.h"
1919
#include "cpprest/http_headers.h"
2020
#include "http_client_impl.h"
21+
#ifdef WIN32
2122
#include <Wincrypt.h>
23+
#endif
24+
#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
25+
#include "winhttppal.h"
26+
#endif
2227
#include <atomic>
2328

24-
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
29+
#if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
2530
#include <VersionHelpers.h>
2631
#endif
2732

@@ -97,7 +102,7 @@ static http::status_code parse_status_code(HINTERNET request_handle)
97102
&buffer[0],
98103
&length,
99104
WINHTTP_NO_HEADER_INDEX);
100-
return (unsigned short)_wtoi(buffer.c_str());
105+
return (unsigned short)stoi(buffer);
101106
}
102107

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

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

167+
162168
class memory_holder
163169
{
164170
uint8_t* m_externalData;
@@ -289,7 +295,7 @@ class winhttp_request_context final : public request_context
289295
{
290296
}
291297

292-
#if defined(_MSC_VER) && _MSC_VER < 1900
298+
#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
293299
compression_state(const compression_state&) = delete;
294300
compression_state(compression_state&& other)
295301
: m_buffer(std::move(other.m_buffer))
@@ -552,6 +558,10 @@ class winhttp_request_context final : public request_context
552558

553559
void on_send_request_validate_cn()
554560
{
561+
#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
562+
// we do the validation inside curl
563+
return;
564+
#else
555565
if (m_customCnCheck.empty())
556566
{
557567
// no custom validation selected; either we've delegated that to winhttp or
@@ -647,6 +657,7 @@ class winhttp_request_context final : public request_context
647657
}
648658

649659
m_cachedEncodedCert.assign(encodedFirst, encodedLast);
660+
#endif
650661
}
651662

652663
protected:
@@ -666,13 +677,13 @@ class winhttp_request_context final : public request_context
666677
winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request)
667678
: request_context(client, request)
668679
, m_request_handle(nullptr)
669-
, m_bodyType(no_body)
670-
, m_startingPosition(std::char_traits<uint8_t>::eof())
671-
, m_body_data()
672-
, m_remaining_to_write(0)
673680
, m_proxy_authentication_tried(false)
674681
, m_server_authentication_tried(false)
682+
, m_bodyType(no_body)
683+
, m_remaining_to_write(0)
684+
, m_startingPosition(std::char_traits<uint8_t>::eof())
675685
, m_readStream(request.body())
686+
, m_body_data()
676687
{
677688
}
678689
};
@@ -731,10 +742,10 @@ class winhttp_client final : public _http_client_communicator
731742
public:
732743
winhttp_client(http::uri address, http_client_config client_config)
733744
: _http_client_communicator(std::move(address), std::move(client_config))
734-
, m_secure(m_uri.scheme() == _XPLATSTR("https"))
735745
, m_opened(false)
736746
, m_hSession(nullptr)
737747
, m_hConnection(nullptr)
748+
, m_secure(m_uri.scheme() == _XPLATSTR("https"))
738749
{
739750
}
740751

@@ -752,7 +763,7 @@ class winhttp_client final : public _http_client_communicator
752763
if (m_hSession != nullptr)
753764
{
754765
// Unregister the callback.
755-
WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL);
766+
WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
756767

757768
WinHttpCloseHandle(m_hSession);
758769
}
@@ -792,8 +803,8 @@ class winhttp_client final : public _http_client_communicator
792803
ie_proxy_config proxyIE;
793804

794805
DWORD access_type;
795-
LPCWSTR proxy_name = WINHTTP_NO_PROXY_NAME;
796-
LPCWSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS;
806+
LPCTSTR proxy_name = WINHTTP_NO_PROXY_NAME;
807+
LPCTSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS;
797808
m_proxy_auto_config = false;
798809
utility::string_t proxy_str;
799810
http::uri uri;
@@ -901,7 +912,7 @@ class winhttp_client final : public _http_client_communicator
901912
}
902913

903914
// Enable TLS 1.1 and 1.2
904-
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
915+
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
905916
BOOL win32_result(FALSE);
906917

907918
DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
@@ -965,6 +976,17 @@ class winhttp_client final : public _http_client_communicator
965976
proxy_info info;
966977
bool proxy_info_required = false;
967978

979+
const auto& method = msg.method();
980+
981+
// stop injection of headers via method
982+
// resource should be ok, since it's been encoded
983+
// and host won't resolve
984+
if (!::web::http::details::validate_method(method))
985+
{
986+
request->report_exception(http_exception("The method string is invalid."));
987+
return;
988+
}
989+
968990
if (m_proxy_auto_config)
969991
{
970992
WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {};
@@ -1416,7 +1438,6 @@ class winhttp_client final : public _http_client_communicator
14161438
{
14171439
return pplx::task_from_exception<size_t>(std::current_exception());
14181440
}
1419-
_ASSERTE(bytes_read >= 0);
14201441

14211442
uint8_t* buffer = p_request_context->m_compression_state.m_acquired;
14221443
if (buffer == nullptr)
@@ -1689,16 +1710,16 @@ class winhttp_client final : public _http_client_communicator
16891710
}
16901711
}
16911712

1692-
static std::wstring get_request_url(HINTERNET hRequestHandle)
1713+
static utility::string_t get_request_url(HINTERNET hRequestHandle)
16931714
{
1694-
std::wstring url;
1715+
utility::string_t url;
16951716
auto urlSize = static_cast<unsigned long>(url.capacity()) * 2; // use initial small string optimization capacity
16961717
for (;;)
16971718
{
1698-
url.resize(urlSize / sizeof(wchar_t));
1699-
if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], &urlSize))
1719+
url.resize(urlSize / sizeof(utility::char_t));
1720+
if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize))
17001721
{
1701-
url.resize(wcslen(url.c_str()));
1722+
url.resize(url.length());
17021723
return url;
17031724
}
17041725

@@ -2014,7 +2035,7 @@ class winhttp_client final : public _http_client_communicator
20142035
// Now allocate buffer for headers and query for them.
20152036
std::vector<unsigned char> header_raw_buffer;
20162037
header_raw_buffer.resize(headerBufferLength);
2017-
utf16char* header_buffer = reinterpret_cast<utf16char*>(&header_raw_buffer[0]);
2038+
utility::char_t* header_buffer = reinterpret_cast<utility::char_t*>(&header_raw_buffer[0]);
20182039
if (!WinHttpQueryHeaders(hRequestHandle,
20192040
WINHTTP_QUERY_RAW_HEADERS_CRLF,
20202041
WINHTTP_HEADER_NAME_BY_INDEX,
@@ -2052,7 +2073,7 @@ class winhttp_client final : public _http_client_communicator
20522073
!p_request_context->m_http_client->client_config().request_compressed_response())
20532074
{
20542075
p_request_context->m_compression_state.m_chunk =
2055-
std::make_unique<winhttp_request_context::compression_state::_chunk_helper>();
2076+
::utility::details::make_unique<winhttp_request_context::compression_state::_chunk_helper>();
20562077
p_request_context->m_compression_state.m_chunked = true;
20572078
}
20582079

@@ -2390,7 +2411,7 @@ class winhttp_client final : public _http_client_communicator
23902411
}).then([p_request_context](pplx::task<bool> op) {
23912412
try
23922413
{
2393-
bool ignored = op.get();
2414+
op.get();
23942415
}
23952416
catch (...)
23962417
{
@@ -2466,6 +2487,7 @@ std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(
24662487
return std::make_shared<details::winhttp_client>(std::move(base_uri), std::move(client_config));
24672488
}
24682489

2490+
24692491
} // namespace details
24702492
} // namespace client
24712493
} // namespace http

Release/src/http/common/http_msg.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,27 +225,27 @@ utility::string_t flatten_http_headers(const http_headers& headers)
225225
return flattened_headers;
226226
}
227227

228-
#if defined(_WIN32)
229-
void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers)
228+
void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers)
230229
{
231-
utf16char* context = nullptr;
232-
utf16char* line = wcstok_s(headersStr, CRLF, &context);
233-
while (line != nullptr)
230+
utility::string_t str(headersStr);
231+
std::size_t pos = str.find_first_of(_XPLATSTR("\r\n"));
232+
std::size_t startpos = 0;
233+
while (pos!=std::string::npos)
234234
{
235-
const utility::string_t header_line(line);
235+
const utility::string_t header_line(str, startpos, pos - startpos);
236236
const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":"));
237237
if (colonIndex != utility::string_t::npos)
238238
{
239239
utility::string_t key = header_line.substr(0, colonIndex);
240240
utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1);
241-
http::details::trim_whitespace(key);
242-
http::details::trim_whitespace(value);
241+
web::http::details::trim_whitespace(key);
242+
web::http::details::trim_whitespace(value);
243243
headers.add(key, value);
244244
}
245-
line = wcstok_s(nullptr, CRLF, &context);
245+
startpos = pos + 1;
246+
pos = str.find_first_of(_XPLATSTR("\r\n"), pos + 1);
246247
}
247248
}
248-
#endif
249249

250250
} // namespace details
251251

0 commit comments

Comments
 (0)