diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt
index e229106951..3467977980 100644
--- a/Release/CMakeLists.txt
+++ b/Release/CMakeLists.txt
@@ -160,6 +160,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
message("-- Setting msvc options")
set(WARNINGS)
+ add_compile_options(/bigobj)
else()
message("-- Unknown compiler, success is doubtful.")
message("CMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}")
diff --git a/Release/include/cpprest/details/http_client_impl.h b/Release/include/cpprest/details/http_client_impl.h
deleted file mode 100644
index 2f88472a50..0000000000
--- a/Release/include/cpprest/details/http_client_impl.h
+++ /dev/null
@@ -1,466 +0,0 @@
-/***
-* ==++==
-*
-* Copyright (c) Microsoft Corporation. All rights reserved.
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-* ==--==
-* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-*
-* HTTP Library: Client-side APIs, non-public declarations used in the implementation.
-*
-* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
-*
-* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-****/
-#pragma once
-
-#include "cpprest/details/basic_types.h"
-#include "cpprest/details/http_helpers.h"
-
-#ifdef _WIN32
-# define CRLF _XPLATSTR("\r\n")
-#else
-# define CRLF std::string("\r\n")
-#endif
-
-namespace web { namespace http { namespace client { namespace details
-{
-
-#ifdef _WIN32
-static const utility::char_t * get_with_body = _XPLATSTR("A GET or HEAD request should not have an entity body.");
-
-// Helper function to trim leading and trailing null characters from a string.
-static void trim_nulls(utility::string_t &str)
-{
- size_t index;
- for(index = 0; index < str.size() && str[index] == 0; ++index);
- str.erase(0, index);
- for(index = str.size(); index > 0 && str[index - 1] == 0; --index);
- str.erase(index);
-}
-
-#endif
-
-// Flatten the http_headers into a name:value pairs separated by a carriage return and line feed.
-static utility::string_t flatten_http_headers(const http_headers &headers)
-{
- utility::string_t flattened_headers;
- for(auto iter = headers.begin(); iter != headers.end(); ++iter)
- {
- flattened_headers.append(iter->first);
- flattened_headers.push_back(':');
- flattened_headers.append(iter->second);
- flattened_headers.append(CRLF);
- }
- return flattened_headers;
-}
-
-#ifdef _WIN32
-///
-/// Parses a string containing Http headers.
-///
-static void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers)
-{
- utf16char *context = nullptr;
- utf16char *line = wcstok_s(headersStr, CRLF, &context);
- while(line != nullptr)
- {
- const utility::string_t header_line(line);
- 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);
- headers.add(key, value);
- }
- line = wcstok_s(nullptr, CRLF, &context);
- }
-}
-#endif
-
-class _http_client_communicator;
-
-// Request context encapsulating everything necessary for creating and responding to a request.
-class request_context
-{
-public:
-
- // Destructor to clean up any held resources.
- virtual ~request_context()
- {
- }
-
- void complete_headers()
- {
- // We have already read (and transmitted) the request body. Should we explicitly close the stream?
- // Well, there are test cases that assumes that the istream is valid when t receives the response!
- // For now, we will drop our reference which will close the stream if the user doesn't have one.
- m_request.set_body(Concurrency::streams::istream());
- m_request_completion.set(m_response);
- }
-
- ///
- /// Completes this request, setting the underlying task completion event, and cleaning up the handles
- ///
- void complete_request(utility::size64_t body_size)
- {
- m_response._get_impl()->_complete(body_size);
-
- finish();
- }
-
- void report_error(unsigned long error_code, const std::string &errorMessage)
- {
- report_exception(http_exception(static_cast(error_code), errorMessage));
- }
-
-#ifdef _WIN32
- void report_error(unsigned long error_code, const std::wstring &errorMessage)
- {
- report_exception(http_exception(static_cast(error_code), errorMessage));
- }
-#endif
-
- template
- void report_exception(const _ExceptionType &e)
- {
- report_exception(std::make_exception_ptr(e));
- }
-
- virtual void report_exception(std::exception_ptr exceptionPtr)
- {
- auto response_impl = m_response._get_impl();
-
- // If cancellation has been triggered then ignore any errors.
- if(m_request._cancellation_token().is_canceled())
- {
- exceptionPtr = std::make_exception_ptr(http_exception((int)std::errc::operation_canceled, std::generic_category()));
- }
-
- // First try to complete the headers with an exception.
- if(m_request_completion.set_exception(exceptionPtr))
- {
- // Complete the request with no msg body. The exception
- // should only be propagated to one of the tce.
- response_impl->_complete(0);
- }
- else
- {
- // Complete the request with an exception
- response_impl->_complete(0, exceptionPtr);
- }
-
- finish();
- }
-
- virtual concurrency::streams::streambuf _get_readbuffer()
- {
- auto instream = m_request.body();
-
- _ASSERTE((bool)instream);
- return instream.streambuf();
- }
-
- concurrency::streams::streambuf _get_writebuffer()
- {
- auto outstream = m_response._get_impl()->outstream();
-
- _ASSERTE((bool)outstream);
- return outstream.streambuf();
- }
-
- // Reference to the http_client implementation.
- std::shared_ptr<_http_client_communicator> m_http_client;
-
- // request/response pair.
- http_request m_request;
- http_response m_response;
-
- utility::size64_t m_uploaded;
- utility::size64_t m_downloaded;
-
- // task completion event to signal request is completed.
- pplx::task_completion_event m_request_completion;
-
- // Registration for cancellation notification if enabled.
- pplx::cancellation_token_registration m_cancellationRegistration;
-
-protected:
-
- request_context(const std::shared_ptr<_http_client_communicator> &client, const http_request &request)
- : m_http_client(client),
- m_request(request),
- m_uploaded(0),
- m_downloaded(0)
- {
- auto responseImpl = m_response._get_impl();
-
- // Copy the user specified output stream over to the response
- responseImpl->set_outstream(request._get_impl()->_response_stream(), false);
-
- // Prepare for receiving data from the network. Ideally, this should be done after
- // we receive the headers and determine that there is a response body. We will do it here
- // since it is not immediately apparent where that would be in the callback handler
- responseImpl->_prepare_to_receive_data();
- }
-
- virtual void finish();
-};
-
-//
-// Interface used by client implementations. Concrete implementations are responsible for
-// sending HTTP requests and receiving the responses.
-//
-class _http_client_communicator
-{
-public:
-
- // Destructor to clean up any held resources.
- virtual ~_http_client_communicator() {}
-
- // Asynchronously send a HTTP request and process the response.
- void async_send_request(const std::shared_ptr &request)
- {
- if(m_client_config.guarantee_order())
- {
- // Send to call block to be processed.
- push_request(request);
- }
- else
- {
- // Schedule a task to start sending.
- pplx::create_task([this, request]
- {
- open_and_send_request(request);
- });
- }
- }
-
- void finish_request()
- {
- // If guarantee order is specified we don't need to do anything.
- if(m_client_config.guarantee_order())
- {
- pplx::extensibility::scoped_critical_section_t l(m_open_lock);
-
- --m_scheduled;
-
- if( !m_requests_queue.empty())
- {
- auto request = m_requests_queue.front();
- m_requests_queue.pop();
-
- // Schedule a task to start sending.
- pplx::create_task([this, request]
- {
- open_and_send_request(request);
- });
- }
- }
- }
-
- const http_client_config& client_config() const
- {
- return m_client_config;
- }
-
- const uri & base_uri() const
- {
- return m_uri;
- }
-
-protected:
- _http_client_communicator(http::uri address, http_client_config client_config)
- : m_uri(std::move(address)), m_client_config(std::move(client_config)), m_opened(false), m_scheduled(0)
- {
- }
-
- // Method to open client.
- virtual unsigned long open() = 0;
-
- // HTTP client implementations must implement send_request.
- virtual void send_request(_In_ const std::shared_ptr &request) = 0;
-
- // URI to connect to.
- const http::uri m_uri;
-
-private:
-
- http_client_config m_client_config;
-
- bool m_opened;
-
- pplx::extensibility::critical_section_t m_open_lock;
-
- // Wraps opening the client around sending a request.
- void open_and_send_request(const std::shared_ptr &request)
- {
- // First see if client needs to be opened.
- auto error = open_if_required();
-
- if (error != 0)
- {
- // Failed to open
- request->report_error(error, _XPLATSTR("Open failed"));
-
- // DO NOT TOUCH the this pointer after completing the request
- // This object could be freed along with the request as it could
- // be the last reference to this object
- return;
- }
-
- send_request(request);
- }
-
- unsigned long open_if_required()
- {
- unsigned long error = 0;
-
- if(!m_opened)
- {
- pplx::extensibility::scoped_critical_section_t l(m_open_lock);
-
- // Check again with the lock held
- if (!m_opened)
- {
- error = open();
-
- if (error == 0)
- {
- m_opened = true;
- }
- }
- }
-
- return error;
- }
-
- void push_request(const std::shared_ptr &request)
- {
- pplx::extensibility::scoped_critical_section_t l(m_open_lock);
-
- if(++m_scheduled == 1)
- {
- // Schedule a task to start sending.
- pplx::create_task([this, request]()
- {
- open_and_send_request(request);
- });
- }
- else
- {
- m_requests_queue.push(request);
- }
- }
-
- // Queue used to guarantee ordering of requests, when applicable.
- std::queue> m_requests_queue;
- int m_scheduled;
-};
-
-inline void request_context::finish()
-{
- // If cancellation is enabled and registration was performed, unregister.
- if(m_cancellationRegistration != pplx::cancellation_token_registration())
- {
- _ASSERTE(m_request._cancellation_token() != pplx::cancellation_token::none());
- m_request._cancellation_token().deregister_callback(m_cancellationRegistration);
- }
-
- m_http_client->finish_request();
-}
-
-class http_network_handler : public http_pipeline_stage
-{
-public:
- http_network_handler(const uri &base_uri, const http_client_config &client_config);
-
- virtual pplx::task propagate(http_request request);
-
- const std::shared_ptr& http_client_impl() const
- {
- return m_http_client_impl;
- }
-
-private:
- std::shared_ptr<_http_client_communicator> m_http_client_impl;
-};
-
-// Helper function to check to make sure the uri is valid.
-void verify_uri(const uri &uri)
-{
- // Some things like proper URI schema are verified by the URI class.
- // We only need to check certain things specific to HTTP.
- if (uri.scheme() != _XPLATSTR("http") && uri.scheme() != _XPLATSTR("https"))
- {
- throw std::invalid_argument("URI scheme must be 'http' or 'https'");
- }
-
- if(uri.host().empty())
- {
- throw std::invalid_argument("URI must contain a hostname.");
- }
-}
-
-} // namespace details
-
-http_client::http_client(const uri &base_uri)
-{
- build_pipeline(base_uri, http_client_config());
-}
-
-http_client::http_client(const uri &base_uri, const http_client_config &client_config)
-{
- build_pipeline(base_uri, client_config);
-}
-
-void http_client::build_pipeline(const uri &base_uri, const http_client_config &client_config)
-{
- if (base_uri.scheme().empty())
- {
- auto uribuilder = uri_builder(base_uri);
- uribuilder.set_scheme(_XPLATSTR("http"));
- uri uriWithScheme = uribuilder.to_uri();
- details::verify_uri(uriWithScheme);
- m_pipeline = ::web::http::http_pipeline::create_pipeline(std::make_shared(uriWithScheme, client_config));
- }
- else
- {
- details::verify_uri(base_uri);
- m_pipeline = ::web::http::http_pipeline::create_pipeline(std::make_shared(base_uri, client_config));
- }
-
-#if !defined(CPPREST_TARGET_XP)
- add_handler(std::static_pointer_cast(
- std::make_shared(client_config.oauth1())));
-#endif
-
- add_handler(std::static_pointer_cast(
- std::make_shared(client_config.oauth2())));
-}
-
-const http_client_config & http_client::client_config() const
-{
- auto ph = std::static_pointer_cast(m_pipeline->last_stage());
- return ph->http_client_impl()->client_config();
-}
-
-const uri & http_client::base_uri() const
-{
- auto ph = std::static_pointer_cast(m_pipeline->last_stage());
- return ph->http_client_impl()->base_uri();
-}
-
-}}} // namespaces
diff --git a/Release/include/cpprest/details/http_helpers.h b/Release/include/cpprest/details/http_helpers.h
index 3e231f5871..0ef034b606 100644
--- a/Release/include/cpprest/details/http_helpers.h
+++ b/Release/include/cpprest/details/http_helpers.h
@@ -27,86 +27,23 @@
****/
#pragma once
-#include "cpprest/http_msg.h"
+#include "cpprest/details/basic_types.h"
namespace web { namespace http
{
namespace details
{
- ///
- /// Constants for MIME types.
- ///
- class mime_types
- {
- public:
- #define _MIME_TYPES
- #define DAT(a,b) _ASYNCRTIMP const static utility::string_t a;
- #include "cpprest/details/http_constants.dat"
- #undef _MIME_TYPES
- #undef DAT
- };
-
- ///
- /// Constants for charset types.
- ///
- class charset_types
- {
- public:
- #define _CHARSET_TYPES
- #define DAT(a,b) _ASYNCRTIMP const static utility::string_t a;
- #include "cpprest/details/http_constants.dat"
- #undef _CHARSET_TYPES
- #undef DAT
- };
-
- ///
- /// Determines whether or not the given content type is 'textual' according the feature specifications.
- ///
- bool is_content_type_textual(const utility::string_t &content_type);
-
- ///
- /// Determines whether or not the given content type is JSON according the feature specifications.
- ///
- bool is_content_type_json(const utility::string_t &content_type);
-
- ///
- /// Parses the given Content-Type header value to get out actual content type and charset.
- /// If the charset isn't specified the default charset for the content type will be set.
- ///
- void parse_content_type_and_charset(const utility::string_t &content_type, utility::string_t &content, utility::string_t &charset);
-
- ///
- /// Gets the default charset for given content type. If the MIME type is not textual or recognized Latin1 will be returned.
- ///
- utility::string_t get_default_charset(const utility::string_t &content_type);
-
///
/// Helper function to get the default HTTP reason phrase for a status code.
///
utility::string_t get_default_reason_phrase(status_code code);
- ///
- /// Helper functions to convert a series of bytes from a charset to utf-8 or utf-16.
- /// These APIs deal with checking for and handling byte order marker (BOM).
- ///
- utility::string_t convert_utf16_to_string_t(utf16string src);
- utf16string convert_utf16_to_utf16(utf16string src);
- std::string convert_utf16_to_utf8(utf16string src);
- utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom);
- std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom);
- utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom);
- std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom);
- utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom);
-
// simple helper functions to trim whitespace.
- _ASYNCRTIMP void __cdecl ltrim_whitespace(utility::string_t &str);
- _ASYNCRTIMP void __cdecl rtrim_whitespace(utility::string_t &str);
_ASYNCRTIMP void __cdecl trim_whitespace(utility::string_t &str);
bool validate_method(const utility::string_t& method);
-
namespace chunked_encoding
{
// Transfer-Encoding: chunked support
diff --git a/Release/include/cpprest/details/x509_cert_utilities.h b/Release/include/cpprest/details/x509_cert_utilities.h
index b9e60df880..5cfa561be5 100644
--- a/Release/include/cpprest/details/x509_cert_utilities.h
+++ b/Release/include/cpprest/details/x509_cert_utilities.h
@@ -25,7 +25,6 @@
#pragma once
-#include
#include
#if defined(__APPLE__) || (defined(ANDROID) || defined(__ANDROID__)) || (defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(_M_ARM) && !defined(CPPREST_EXCLUDE_WEBSOCKETS))
@@ -52,13 +51,11 @@ namespace web { namespace http { namespace client { namespace details {
/// Using platform specific APIs verifies server certificate.
/// Currently implemented to work on iOS, Android, and OS X.
///
-/// Boost.ASIO context get certificate chain from.
+/// Boost.ASIO context to get certificate chain from.
/// Host name from the URI.
/// True if verification passed and server can be trusted, false otherwise.
bool verify_cert_chain_platform_specific(boost::asio::ssl::verify_context &verifyCtx, const std::string &hostName);
-bool verify_X509_cert_chain(const std::vector &certChain, const std::string &hostName);
-
}}}}
#endif
\ No newline at end of file
diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h
index 1e0ce8b73f..0c8d7e9f8c 100644
--- a/Release/include/cpprest/http_client.h
+++ b/Release/include/cpprest/http_client.h
@@ -280,8 +280,7 @@ class http_client_config
}
#endif
-#ifdef _WIN32
-#if !defined(__cplusplus_winrt)
+#if defined(_WIN32) && !defined(__cplusplus_winrt)
///
/// Checks if request data buffering is turned on, the default is off.
///
@@ -302,7 +301,6 @@ class http_client_config
{
m_buffer_request = buffer_request;
}
-#endif
#endif
///
@@ -399,6 +397,8 @@ class http_client_config
#endif
};
+class http_pipeline;
+
///
/// HTTP client class, used to maintain a connection to an HTTP service for an extended session.
///
@@ -422,7 +422,7 @@ class http_client
/// Note the destructor doesn't necessarily close the connection and release resources.
/// The connection is reference counted with the http_responses.
///
- ~http_client() CPPREST_NOEXCEPT {}
+ _ASYNCRTIMP ~http_client() CPPREST_NOEXCEPT;
///
/// Gets the base URI.
@@ -442,19 +442,14 @@ class http_client
/// Adds an HTTP pipeline stage to the client.
///
/// A function object representing the pipeline stage.
- void add_handler(const std::function(http_request, std::shared_ptr)> &handler)
- {
- m_pipeline->append(std::make_shared<::web::http::details::function_pipeline_wrapper>(handler));
- }
+ _ASYNCRTIMP void add_handler(const std::function(http_request, std::shared_ptr)> &handler);
+
///
/// Adds an HTTP pipeline stage to the client.
///
/// A shared pointer to a pipeline stage.
- void add_handler(const std::shared_ptr &stage)
- {
- m_pipeline->append(stage);
- }
+ _ASYNCRTIMP void add_handler(const std::shared_ptr &stage);
///
/// Asynchronously sends an HTTP request.
@@ -727,11 +722,16 @@ class http_client
private:
- void build_pipeline(const uri &base_uri, const http_client_config &client_config);
-
std::shared_ptr<::web::http::http_pipeline> m_pipeline;
};
+namespace details {
+#if defined(_WIN32)
+extern const utility::char_t * get_with_body_err_msg;
+#endif
+
+}
+
}}}
#endif
diff --git a/Release/include/cpprest/http_headers.h b/Release/include/cpprest/http_headers.h
index 8228d7cccb..388fb4bd1c 100644
--- a/Release/include/cpprest/http_headers.h
+++ b/Release/include/cpprest/http_headers.h
@@ -328,4 +328,18 @@ class http_headers
std::map m_headers;
};
+namespace details {
+
+ ///
+ /// Serialize the http_headers into name:value pairs separated by a carriage return and line feed.
+ ///
+ utility::string_t flatten_http_headers(const http_headers &headers);
+#if defined(_WIN32)
+ ///
+ /// Parses a string containing Http headers.
+ ///
+ void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers);
+#endif
+}
+
}}
\ No newline at end of file
diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h
index 9ad32d937b..b2721f9ebc 100644
--- a/Release/include/cpprest/http_msg.h
+++ b/Release/include/cpprest/http_msg.h
@@ -89,6 +89,36 @@ class status_codes
#undef DAT
};
+namespace details {
+
+///
+/// Constants for MIME types.
+///
+class mime_types
+{
+public:
+#define _MIME_TYPES
+#define DAT(a,b) _ASYNCRTIMP const static utility::string_t a;
+#include "cpprest/details/http_constants.dat"
+#undef _MIME_TYPES
+#undef DAT
+};
+
+///
+/// Constants for charset types.
+///
+class charset_types
+{
+public:
+#define _CHARSET_TYPES
+#define DAT(a,b) _ASYNCRTIMP const static utility::string_t a;
+#include "cpprest/details/http_constants.dat"
+#undef _CHARSET_TYPES
+#undef DAT
+};
+
+}
+
/// Message direction
namespace message_direction
{
@@ -346,7 +376,7 @@ class _http_server_context
///
/// Internal representation of an HTTP response.
///
-class _http_response : public http::details::http_msg_base
+class _http_response final : public http::details::http_msg_base
{
public:
_http_response() : m_status_code((std::numeric_limits::max)()) { }
@@ -458,7 +488,7 @@ class http_response
/// Extracts the body of the response message as a string value, checking that the content type is a MIME text type.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
///
- /// If true, ignores the Content-Type header and assumes UTF-8.
+ /// If true, ignores the Content-Type header and assumes text.
/// String containing body of the message.
pplx::task extract_string(bool ignore_content_type = false) const
{
@@ -470,7 +500,7 @@ class http_response
/// Extracts the body of the response message as a UTF-8 string value, checking that the content type is a MIME text type.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
///
- /// If true, ignores the Content-Type header and assumes UTF-8.
+ /// If true, ignores the Content-Type header and assumes text.
/// String containing body of the message.
pplx::task extract_utf8string(bool ignore_content_type = false) const
{
@@ -482,7 +512,7 @@ class http_response
/// Extracts the body of the response message as a UTF-16 string value, checking that the content type is a MIME text type.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
///
- /// If true, ignores the Content-Type header and assumes UTF-16.
+ /// If true, ignores the Content-Type header and assumes text.
/// String containing body of the message.
pplx::task extract_utf16string(bool ignore_content_type = false) const
{
@@ -494,7 +524,7 @@ class http_response
/// Extracts the body of the response message into a json value, checking that the content type is application/json.
/// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
///
- /// If true, ignores the Content-Type header and assumes UTF-8.
+ /// If true, ignores the Content-Type header and assumes json.
/// JSON value from the body of this message.
pplx::task extract_json(bool ignore_content_type = false) const
{
@@ -677,7 +707,7 @@ namespace details {
///
/// Internal representation of an HTTP request message.
///
-class _http_request : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request>
+class _http_request final : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request>
{
public:
@@ -1288,6 +1318,10 @@ class http_request
std::shared_ptr _m_impl;
};
+namespace client {
+class http_pipeline;
+}
+
///
/// HTTP client handler class, used to represent an HTTP pipeline stage.
///
@@ -1303,9 +1337,12 @@ class http_pipeline_stage : public std::enable_shared_from_this
/// Runs this stage against the given request and passes onto the next stage.
@@ -1316,10 +1353,6 @@ class http_pipeline_stage : public std::enable_shared_from_this
/// Gets the next stage in the pipeline.
///
@@ -1333,13 +1366,14 @@ class http_pipeline_stage : public std::enable_shared_from_this
/// A shared pointer to a pipeline stage.
+ CASABLANCA_DEPRECATED("This api is redundant. Use 'shared_from_this()' directly instead.")
std::shared_ptr current_stage()
{
return this->shared_from_this();
}
private:
- friend class http_pipeline;
+ friend class ::web::http::client::http_pipeline;
void set_next_stage(const std::shared_ptr &next)
{
@@ -1348,116 +1382,6 @@ class http_pipeline_stage : public std::enable_shared_from_this m_next_stage;
- // No copy or assignment.
- http_pipeline_stage & operator=(const http_pipeline_stage &);
- http_pipeline_stage(const http_pipeline_stage &);
-};
-
-namespace details {
-
-class function_pipeline_wrapper : public http::http_pipeline_stage
-{
-public:
- function_pipeline_wrapper(std::function(http_request, std::shared_ptr)> handler) : m_handler(handler)
- {
- }
-
- virtual pplx::task propagate(http_request request) override
- {
- return m_handler(request, next_stage());
- }
-private:
-
- std::function(http_request, std::shared_ptr)> m_handler;
-};
-
-} // namespace details
-
-///
-///
-///
-class http_pipeline
-{
-public:
-
- ///
- /// Create an http pipeline that consists of a linear chain of stages
- ///
- /// The final stage
- static std::shared_ptr create_pipeline(const std::shared_ptr &last)
- {
- return std::shared_ptr(new http_pipeline(last));
- }
-
- ///
- /// Initiate an http request into the pipeline
- ///
- /// Http request
- pplx::task propagate(http_request request)
- {
- std::shared_ptr first;
- {
- pplx::extensibility::scoped_recursive_lock_t l(m_lock);
- first = (m_stages.size() > 0) ? m_stages[0] : m_last_stage;
- }
- return first->propagate(request);
- }
-
- ///
- /// Adds an HTTP pipeline stage to the pipeline.
- ///
- /// A pipeline stage.
- void append(const std::shared_ptr &stage)
- {
- pplx::extensibility::scoped_recursive_lock_t l(m_lock);
-
- if (m_stages.size() > 0)
- {
- std::shared_ptr penultimate = m_stages[m_stages.size()-1];
- penultimate->set_next_stage(stage);
- }
- stage->set_next_stage(m_last_stage);
-
- m_stages.push_back(stage);
- }
-
- ///
- /// Sets the last stage of the pipeline.
- ///
- /// Shared pointer to pipeline stage to set as the last.
- void set_last_stage(const std::shared_ptr &last)
- {
- m_last_stage = last;
- }
-
- ///
- /// Retrieves the last stage in this pipeline.
- ///
- /// A shared pointer to last stage.
- const std::shared_ptr& last_stage() const
- {
- return m_last_stage;
- }
-
-private:
-
- http_pipeline(const std::shared_ptr &last) : m_last_stage(last)
- {
- }
-
- // The vector of pipeline stages.
- std::vector> m_stages;
-
- // The last stage is always set up by the client or listener and cannot
- // be changed. All application-defined stages are executed before the
- // last stage, which is typically a send or dispatch.
- std::shared_ptr m_last_stage;
-
- pplx::extensibility::recursive_lock_t m_lock;
-
- // No copy or assignment.
- http_pipeline & operator=(const http_pipeline &);
- http_pipeline(const http_pipeline &);
};
}}
diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt
index ade7c93b5b..eba83b2cc3 100644
--- a/Release/src/CMakeLists.txt
+++ b/Release/src/CMakeLists.txt
@@ -4,6 +4,7 @@ if (NOT CPPREST_EXCLUDE_WEBSOCKETS)
endif()
set(SOURCES_COMMON
+ http/client/http_client.cpp
http/client/http_client_msg.cpp
http/client/x509_cert_utilities.cpp
http/common/http_helpers.cpp
@@ -40,14 +41,12 @@ if(UNIX)
if(APPLE)
list(APPEND SOURCES
pplx/pplxapple.cpp
- http/client/x509_cert_utilities_apple.cpp
)
find_library(COREFOUNDATION CoreFoundation "/")
find_library(SECURITY Security "/")
set(EXTRALINKS ${COREFOUNDATION} ${SECURITY})
elseif(ANDROID)
list(APPEND SOURCES
- http/client/x509_cert_utilities_android.cpp
pplx/pplxlinux.cpp
)
else()
@@ -62,7 +61,6 @@ elseif(WIN32)
set(SOURCES
${SOURCES_COMMON}
http/client/http_client_winhttp.cpp
- http/client/x509_cert_utilities_win32.cpp
http/listener/http_server_httpsys.cpp
pplx/pplxwin.cpp
streams/fileio_win32.cpp
diff --git a/Release/src/build/android.vcxitems b/Release/src/build/android.vcxitems
index e6febf2f9e..b838706244 100644
--- a/Release/src/build/android.vcxitems
+++ b/Release/src/build/android.vcxitems
@@ -15,7 +15,6 @@
-
diff --git a/Release/src/build/android.vcxitems.filters b/Release/src/build/android.vcxitems.filters
index 28951ec63a..a79b9de949 100644
--- a/Release/src/build/android.vcxitems.filters
+++ b/Release/src/build/android.vcxitems.filters
@@ -19,9 +19,6 @@
Source Files
-
- Source Files
-
diff --git a/Release/src/build/common.vcxitems b/Release/src/build/common.vcxitems
index 28702632db..eec185f2ee 100644
--- a/Release/src/build/common.vcxitems
+++ b/Release/src/build/common.vcxitems
@@ -15,6 +15,7 @@
+
@@ -51,12 +52,11 @@
-
-
+
@@ -83,6 +83,7 @@
+
diff --git a/Release/src/build/common.vcxitems.filters b/Release/src/build/common.vcxitems.filters
index 4133dbbe93..35cf8139d2 100644
--- a/Release/src/build/common.vcxitems.filters
+++ b/Release/src/build/common.vcxitems.filters
@@ -67,6 +67,9 @@
Source Files
+
+ Source Files
+
@@ -87,6 +90,9 @@
{1c12997c-5bf5-4b60-853e-a5f9c8303760}
+
+ {97da7aee-41c8-4948-bb0e-c31cec1bfb16}
+
@@ -152,9 +158,6 @@
Header Files\cpprest\details
-
- Header Files\cpprest\details
-
Header Files\cpprest\details
@@ -209,6 +212,9 @@
Header Files\cpprest\details
+
+ Header Files\private
+
diff --git a/Release/src/build/other.vcxitems b/Release/src/build/other.vcxitems
index ff29aaf2bb..2199600b11 100644
--- a/Release/src/build/other.vcxitems
+++ b/Release/src/build/other.vcxitems
@@ -14,7 +14,6 @@
-
diff --git a/Release/src/build/other.vcxitems.filters b/Release/src/build/other.vcxitems.filters
index 486bba9f26..caa96f0b2a 100644
--- a/Release/src/build/other.vcxitems.filters
+++ b/Release/src/build/other.vcxitems.filters
@@ -12,8 +12,5 @@
Source Files
-
- Source Files
-
\ No newline at end of file
diff --git a/Release/src/build/win32.vcxitems b/Release/src/build/win32.vcxitems
index 5de0ce00ad..0d9e515317 100644
--- a/Release/src/build/win32.vcxitems
+++ b/Release/src/build/win32.vcxitems
@@ -15,7 +15,6 @@
-
diff --git a/Release/src/build/win32.vcxitems.filters b/Release/src/build/win32.vcxitems.filters
index 10063c1013..f32d09f1d2 100644
--- a/Release/src/build/win32.vcxitems.filters
+++ b/Release/src/build/win32.vcxitems.filters
@@ -13,9 +13,6 @@
Source Files
-
- Source Files
-
Source Files
diff --git a/Release/src/http/client/http_client.cpp b/Release/src/http/client/http_client.cpp
new file mode 100644
index 0000000000..d9676024ee
--- /dev/null
+++ b/Release/src/http/client/http_client.cpp
@@ -0,0 +1,422 @@
+/***
+* ==++==
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+* ==--==
+* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+*
+* HTTP Library: Client-side APIs.
+*
+* This file contains shared code across all http_client implementations.
+*
+* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+*
+* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+****/
+
+#include "stdafx.h"
+
+#include "http_client_impl.h"
+
+namespace web { namespace http { namespace client {
+
+// Helper function to check to make sure the uri is valid.
+static void verify_uri(const uri &uri)
+{
+ // Some things like proper URI schema are verified by the URI class.
+ // We only need to check certain things specific to HTTP.
+ if (uri.scheme() != _XPLATSTR("http") && uri.scheme() != _XPLATSTR("https"))
+ {
+ throw std::invalid_argument("URI scheme must be 'http' or 'https'");
+ }
+
+ if (uri.host().empty())
+ {
+ throw std::invalid_argument("URI must contain a hostname.");
+ }
+}
+
+namespace details
+{
+
+#if defined(_WIN32)
+ extern const utility::char_t * get_with_body_err_msg = _XPLATSTR("A GET or HEAD request should not have an entity body.");
+#endif
+
+void request_context::complete_headers()
+{
+ // We have already read (and transmitted) the request body. Should we explicitly close the stream?
+ // Well, there are test cases that assumes that the istream is valid when t receives the response!
+ // For now, we will drop our reference which will close the stream if the user doesn't have one.
+ m_request.set_body(Concurrency::streams::istream());
+ m_request_completion.set(m_response);
+}
+
+void request_context::complete_request(utility::size64_t body_size)
+{
+ m_response._get_impl()->_complete(body_size);
+
+ finish();
+}
+
+void request_context::report_error(unsigned long error_code, const std::string &errorMessage)
+{
+ report_exception(http_exception(static_cast(error_code), errorMessage));
+}
+
+#if defined(_WIN32)
+void request_context::report_error(unsigned long error_code, const std::wstring &errorMessage)
+{
+ report_exception(http_exception(static_cast(error_code), errorMessage));
+}
+#endif
+
+void request_context::report_exception(std::exception_ptr exceptionPtr)
+{
+ auto response_impl = m_response._get_impl();
+
+ // If cancellation has been triggered then ignore any errors.
+ if (m_request._cancellation_token().is_canceled())
+ {
+ exceptionPtr = std::make_exception_ptr(http_exception((int)std::errc::operation_canceled, std::generic_category()));
+ }
+
+ // First try to complete the headers with an exception.
+ if (m_request_completion.set_exception(exceptionPtr))
+ {
+ // Complete the request with no msg body. The exception
+ // should only be propagated to one of the tce.
+ response_impl->_complete(0);
+ }
+ else
+ {
+ // Complete the request with an exception
+ response_impl->_complete(0, exceptionPtr);
+ }
+
+ finish();
+}
+
+concurrency::streams::streambuf request_context::_get_readbuffer()
+{
+ auto instream = m_request.body();
+
+ _ASSERTE((bool)instream);
+ return instream.streambuf();
+}
+
+concurrency::streams::streambuf request_context::_get_writebuffer()
+{
+ auto outstream = m_response._get_impl()->outstream();
+
+ _ASSERTE((bool)outstream);
+ return outstream.streambuf();
+}
+
+request_context::request_context(const std::shared_ptr<_http_client_communicator> &client, const http_request &request)
+ : m_http_client(client),
+ m_request(request),
+ m_uploaded(0),
+ m_downloaded(0)
+{
+ auto responseImpl = m_response._get_impl();
+
+ // Copy the user specified output stream over to the response
+ responseImpl->set_outstream(request._get_impl()->_response_stream(), false);
+
+ // Prepare for receiving data from the network. Ideally, this should be done after
+ // we receive the headers and determine that there is a response body. We will do it here
+ // since it is not immediately apparent where that would be in the callback handler
+ responseImpl->_prepare_to_receive_data();
+}
+
+void _http_client_communicator::async_send_request(const std::shared_ptr &request)
+{
+ if (m_client_config.guarantee_order())
+ {
+ // Send to call block to be processed.
+ push_request(request);
+ }
+ else
+ {
+ // Schedule a task to start sending.
+ pplx::create_task([this, request]
+ {
+ open_and_send_request(request);
+ });
+ }
+}
+
+void _http_client_communicator::finish_request()
+{
+ // If guarantee order is specified we don't need to do anything.
+ if (m_client_config.guarantee_order())
+ {
+ pplx::extensibility::scoped_critical_section_t l(m_open_lock);
+
+ --m_scheduled;
+
+ if (!m_requests_queue.empty())
+ {
+ auto request = m_requests_queue.front();
+ m_requests_queue.pop();
+
+ // Schedule a task to start sending.
+ pplx::create_task([this, request]
+ {
+ open_and_send_request(request);
+ });
+ }
+ }
+}
+
+const http_client_config& _http_client_communicator::client_config() const
+{
+ return m_client_config;
+}
+
+const uri & _http_client_communicator::base_uri() const
+{
+ return m_uri;
+}
+
+_http_client_communicator::_http_client_communicator(http::uri address, http_client_config client_config)
+ : m_uri(std::move(address)), m_client_config(std::move(client_config)), m_opened(false), m_scheduled(0)
+{
+}
+
+// Wraps opening the client around sending a request.
+void _http_client_communicator::open_and_send_request(const std::shared_ptr &request)
+{
+ // First see if client needs to be opened.
+ auto error = open_if_required();
+
+ if (error != 0)
+ {
+ // Failed to open
+ request->report_error(error, _XPLATSTR("Open failed"));
+
+ // DO NOT TOUCH the this pointer after completing the request
+ // This object could be freed along with the request as it could
+ // be the last reference to this object
+ return;
+ }
+
+ send_request(request);
+}
+
+unsigned long _http_client_communicator::open_if_required()
+{
+ unsigned long error = 0;
+
+ if (!m_opened)
+ {
+ pplx::extensibility::scoped_critical_section_t l(m_open_lock);
+
+ // Check again with the lock held
+ if (!m_opened)
+ {
+ error = open();
+
+ if (error == 0)
+ {
+ m_opened = true;
+ }
+ }
+ }
+
+ return error;
+}
+
+void _http_client_communicator::push_request(const std::shared_ptr &request)
+{
+ pplx::extensibility::scoped_critical_section_t l(m_open_lock);
+
+ if (++m_scheduled == 1)
+ {
+ // Schedule a task to start sending.
+ pplx::create_task([this, request]()
+ {
+ open_and_send_request(request);
+ });
+ }
+ else
+ {
+ m_requests_queue.push(request);
+ }
+}
+
+inline void request_context::finish()
+{
+ // If cancellation is enabled and registration was performed, unregister.
+ if (m_cancellationRegistration != pplx::cancellation_token_registration())
+ {
+ _ASSERTE(m_request._cancellation_token() != pplx::cancellation_token::none());
+ m_request._cancellation_token().deregister_callback(m_cancellationRegistration);
+ }
+
+ m_http_client->finish_request();
+}
+
+} // namespace details
+
+///
+/// Private implementation of http_client. Manages the http request processing pipeline.
+///
+class http_pipeline
+{
+public:
+ http_pipeline(std::shared_ptr last) : m_last_stage(std::move(last))
+ {}
+
+ // pplx::extensibility::recursive_lock_t does not support move/copy, but does not delete the functions either.
+ http_pipeline(const http_pipeline &) = delete;
+ http_pipeline(http_pipeline &&) = delete;
+ http_pipeline & operator=(const http_pipeline &) = delete;
+ http_pipeline & operator=(http_pipeline &&) = delete;
+
+ ///
+ /// Initiate an http request into the pipeline
+ ///
+ /// Http request
+ pplx::task propagate(http_request request)
+ {
+ std::shared_ptr first;
+ {
+ pplx::extensibility::scoped_recursive_lock_t l(m_lock);
+ first = (m_stages.size() > 0) ? m_stages[0] : m_last_stage;
+ }
+ return first->propagate(request);
+ }
+
+ ///
+ /// Adds an HTTP pipeline stage to the pipeline.
+ ///
+ /// A pipeline stage.
+ void append(const std::shared_ptr &stage)
+ {
+ pplx::extensibility::scoped_recursive_lock_t l(m_lock);
+
+ if (m_stages.size() > 0)
+ {
+ std::shared_ptr penultimate = m_stages[m_stages.size() - 1];
+ penultimate->set_next_stage(stage);
+ }
+ stage->set_next_stage(m_last_stage);
+
+ m_stages.push_back(stage);
+ }
+
+ // The last stage is always set up by the client or listener and cannot
+ // be changed. All application-defined stages are executed before the
+ // last stage, which is typically a send or dispatch.
+ const std::shared_ptr m_last_stage;
+
+private:
+
+ // The vector of pipeline stages.
+ std::vector> m_stages;
+
+ pplx::extensibility::recursive_lock_t m_lock;
+};
+
+void http_client::add_handler(const std::function(http_request, std::shared_ptr)> &handler)
+{
+ class function_pipeline_wrapper : public http::http_pipeline_stage
+ {
+ public:
+ function_pipeline_wrapper(const std::function(http_request, std::shared_ptr)> &handler) : m_handler(handler)
+ {
+ }
+
+ virtual pplx::task propagate(http_request request) override
+ {
+ return m_handler(std::move(request), next_stage());
+ }
+ private:
+
+ std::function(http_request, std::shared_ptr)> m_handler;
+ };
+
+ m_pipeline->append(std::make_shared(handler));
+}
+
+void http_client::add_handler(const std::shared_ptr &stage)
+{
+ m_pipeline->append(stage);
+}
+
+http_client::http_client(const uri &base_uri) : http_client(base_uri, http_client_config())
+{}
+
+http_client::http_client(const uri &base_uri, const http_client_config &client_config)
+{
+ std::shared_ptr final_pipeline_stage;
+
+ if (base_uri.scheme().empty())
+ {
+ auto uribuilder = uri_builder(base_uri);
+ uribuilder.set_scheme(_XPLATSTR("http"));
+ uri uriWithScheme = uribuilder.to_uri();
+ verify_uri(uriWithScheme);
+ final_pipeline_stage = details::create_platform_final_pipeline_stage(uriWithScheme, client_config);
+ }
+ else
+ {
+ verify_uri(base_uri);
+ final_pipeline_stage = details::create_platform_final_pipeline_stage(base_uri, client_config);
+ }
+
+ m_pipeline = std::make_shared(std::move(final_pipeline_stage));
+
+#if !defined(CPPREST_TARGET_XP)
+ add_handler(std::static_pointer_cast(
+ std::make_shared(client_config.oauth1())));
+#endif
+
+ add_handler(std::static_pointer_cast(
+ std::make_shared(client_config.oauth2())));
+}
+
+http_client::~http_client() CPPREST_NOEXCEPT {}
+
+const http_client_config & http_client::client_config() const
+{
+ return m_pipeline->m_last_stage->client_config();
+}
+
+const uri & http_client::base_uri() const
+{
+ return m_pipeline->m_last_stage->base_uri();
+}
+
+// Macros to help build string at compile time and avoid overhead.
+#define STRINGIFY(x) _XPLATSTR(#x)
+#define TOSTRING(x) STRINGIFY(x)
+#define USERAGENT _XPLATSTR("cpprestsdk/") TOSTRING(CPPREST_VERSION_MAJOR) _XPLATSTR(".") TOSTRING(CPPREST_VERSION_MINOR) _XPLATSTR(".") TOSTRING(CPPREST_VERSION_REVISION)
+
+pplx::task http_client::request(http_request request, const pplx::cancellation_token &token)
+{
+ if (!request.headers().has(header_names::user_agent))
+ {
+ request.headers().add(header_names::user_agent, USERAGENT);
+ }
+
+ request._set_base_uri(base_uri());
+ request._set_cancellation_token(token);
+ return m_pipeline->propagate(request);
+}
+
+
+}}}
\ No newline at end of file
diff --git a/Release/src/http/client/http_client_asio.cpp b/Release/src/http/client/http_client_asio.cpp
index 44296040e0..72eeb0d8df 100644
--- a/Release/src/http/client/http_client_asio.cpp
+++ b/Release/src/http/client/http_client_asio.cpp
@@ -43,12 +43,15 @@
#error "Cpp rest SDK requires c++11 smart pointer support from boost"
#endif
-#include "cpprest/details/http_client_impl.h"
+#include "http_client_impl.h"
+#include "cpprest/base_uri.h"
#include "cpprest/details/x509_cert_utilities.h"
#include
using boost::asio::ip::tcp;
+#define CRLF std::string("\r\n")
+
namespace web { namespace http
{
namespace client
@@ -227,8 +230,6 @@ class asio_connection
m_is_reused = true;
}
- void handle_pool_timer(const boost::system::error_code& ec);
-
// Guards concurrent access to socket/ssl::stream. This is necessary
// because timeouts and cancellation can touch the socket at the same time
// as normal message processing.
@@ -331,7 +332,7 @@ class asio_connection_pool
-class asio_client : public _http_client_communicator, public std::enable_shared_from_this
+class asio_client : public _http_client_communicator
{
public:
asio_client(http::uri address, http_client_config client_config)
@@ -347,6 +348,8 @@ class asio_client : public _http_client_communicator, public std::enable_shared_
unsigned long open() override { return 0; }
+ virtual pplx::task propagate(http_request request) override;
+
asio_connection_pool m_pool;
tcp::resolver m_resolver;
};
@@ -656,7 +659,7 @@ class asio_context : public request_context, public std::enable_shared_from_this
extra_headers.append(": no-cache" + CRLF);
}
- request_stream << flatten_http_headers(ctx->m_request.headers());
+ request_stream << ::web::http::details::flatten_http_headers(ctx->m_request.headers());
request_stream << extra_headers;
// Enforce HTTP connection keep alive (even for the old HTTP/1.0 protocol).
request_stream << "Connection: Keep-Alive" << CRLF << CRLF;
@@ -1446,22 +1449,9 @@ class asio_context : public request_context, public std::enable_shared_from_this
};
-
-http_network_handler::http_network_handler(const uri &base_uri, const http_client_config &client_config) :
- m_http_client_impl(std::make_shared(base_uri, client_config))
-{}
-
-pplx::task http_network_handler::propagate(http_request request)
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri base_uri, const http_client_config& client_config)
{
- auto context = details::asio_context::create_request_context(m_http_client_impl, request);
-
- // Use a task to externally signal the final result and completion of the task.
- auto result_task = pplx::create_task(context->m_request_completion);
-
- // Asynchronously send the response with the HTTP client implementation.
- m_http_client_impl->async_send_request(context);
-
- return result_task;
+ return std::make_shared(base_uri, client_config);
}
void asio_client::send_request(const std::shared_ptr &request_ctx)
@@ -1488,4 +1478,18 @@ void asio_client::send_request(const std::shared_ptr &request_c
ctx->start_request();
}
+pplx::task asio_client::propagate(http_request request)
+{
+ auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this());
+ auto context = details::asio_context::create_request_context(self, request);
+
+ // Use a task to externally signal the final result and completion of the task.
+ auto result_task = pplx::create_task(context->m_request_completion);
+
+ // Asynchronously send the response with the HTTP client implementation.
+ this->async_send_request(context);
+
+ return result_task;
+}
+
}}}} // namespaces
diff --git a/Release/src/http/client/http_client_impl.h b/Release/src/http/client/http_client_impl.h
new file mode 100644
index 0000000000..ac0ae81ce1
--- /dev/null
+++ b/Release/src/http/client/http_client_impl.h
@@ -0,0 +1,151 @@
+/***
+* ==++==
+*
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+* ==--==
+* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+*
+* HTTP Library: Client-side APIs.
+*
+* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
+*
+* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+****/
+#pragma once
+
+#include "cpprest/details/basic_types.h"
+#include "cpprest/astreambuf.h"
+#include "cpprest/http_client.h"
+#include "cpprest/http_msg.h"
+#include
+#include
+#include
+
+namespace web { namespace http { namespace client { namespace details {
+
+class _http_client_communicator;
+
+// Request context encapsulating everything necessary for creating and responding to a request.
+class request_context
+{
+public:
+
+ // Destructor to clean up any held resources.
+ virtual ~request_context() {}
+
+ virtual void report_exception(std::exception_ptr exceptionPtr);
+
+ virtual concurrency::streams::streambuf _get_readbuffer();
+
+ void complete_headers();
+
+ ///
+ /// Completes this request, setting the underlying task completion event, and cleaning up the handles
+ ///
+ void complete_request(utility::size64_t body_size);
+
+ void report_error(unsigned long error_code, const std::string &errorMessage);
+
+#ifdef _WIN32
+ void report_error(unsigned long error_code, const std::wstring &errorMessage);
+#endif
+
+ template
+ void report_exception(const _ExceptionType &e)
+ {
+ report_exception(std::make_exception_ptr(e));
+ }
+
+ concurrency::streams::streambuf _get_writebuffer();
+
+ // Reference to the http_client implementation.
+ std::shared_ptr<_http_client_communicator> m_http_client;
+
+ // request/response pair.
+ http_request m_request;
+ http_response m_response;
+
+ utility::size64_t m_uploaded;
+ utility::size64_t m_downloaded;
+
+ // task completion event to signal request is completed.
+ pplx::task_completion_event m_request_completion;
+
+ // Registration for cancellation notification if enabled.
+ pplx::cancellation_token_registration m_cancellationRegistration;
+
+protected:
+
+ request_context(const std::shared_ptr<_http_client_communicator> &client, const http_request &request);
+
+ virtual void finish();
+};
+
+//
+// Interface used by client implementations. Concrete implementations are responsible for
+// sending HTTP requests and receiving the responses.
+//
+class _http_client_communicator : public http_pipeline_stage
+{
+public:
+
+ virtual ~_http_client_communicator() {}
+
+ // Asynchronously send a HTTP request and process the response.
+ void async_send_request(const std::shared_ptr &request);
+
+ void finish_request();
+
+ const http_client_config& client_config() const;
+
+ const uri & base_uri() const;
+
+protected:
+ _http_client_communicator(http::uri address, http_client_config client_config);
+
+ // Method to open client.
+ virtual unsigned long open() = 0;
+
+ // HTTP client implementations must implement send_request.
+ virtual void send_request(_In_ const std::shared_ptr &request) = 0;
+
+ // URI to connect to.
+ const http::uri m_uri;
+
+private:
+
+ http_client_config m_client_config;
+
+ bool m_opened;
+
+ pplx::extensibility::critical_section_t m_open_lock;
+
+ // Wraps opening the client around sending a request.
+ void open_and_send_request(const std::shared_ptr &request);
+
+ unsigned long open_if_required();
+
+ void push_request(const std::shared_ptr &request);
+
+ // Queue used to guarantee ordering of requests, when applicable.
+ std::queue> m_requests_queue;
+ int m_scheduled;
+};
+
+///
+/// Factory function implemented by the separate platforms to construct their subclasses of _http_client_communicator
+///
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri base_uri, const http_client_config& client_config);
+
+}}}}
\ No newline at end of file
diff --git a/Release/src/http/client/http_client_msg.cpp b/Release/src/http/client/http_client_msg.cpp
index 0a73670a47..dd89d8fffa 100644
--- a/Release/src/http/client/http_client_msg.cpp
+++ b/Release/src/http/client/http_client_msg.cpp
@@ -97,21 +97,4 @@ utility::string_t details::_http_response::to_string() const
return buffer.str();
}
-// Macros to help build string at compile time and avoid overhead.
-#define STRINGIFY(x) _XPLATSTR(#x)
-#define TOSTRING(x) STRINGIFY(x)
-#define USERAGENT _XPLATSTR("cpprestsdk/") TOSTRING(CPPREST_VERSION_MAJOR) _XPLATSTR(".") TOSTRING(CPPREST_VERSION_MINOR) _XPLATSTR(".") TOSTRING(CPPREST_VERSION_REVISION)
-
-pplx::task client::http_client::request(http_request request, const pplx::cancellation_token &token)
-{
- if(!request.headers().has(header_names::user_agent))
- {
- request.headers().add(header_names::user_agent, USERAGENT);
- }
-
- request._set_base_uri(base_uri());
- request._set_cancellation_token(token);
- return m_pipeline->propagate(request);
-}
-
}} // namespace web::http
diff --git a/Release/src/http/client/http_client_winhttp.cpp b/Release/src/http/client/http_client_winhttp.cpp
index 36f596b937..e4d19042b2 100644
--- a/Release/src/http/client/http_client_winhttp.cpp
+++ b/Release/src/http/client/http_client_winhttp.cpp
@@ -26,7 +26,8 @@
****/
#include "stdafx.h"
-#include "cpprest/details/http_client_impl.h"
+#include "cpprest/http_headers.h"
+#include "http_client_impl.h"
namespace web
{
@@ -66,6 +67,16 @@ static http::status_code parse_status_code(HINTERNET request_handle)
return (unsigned short)_wtoi(buffer.c_str());
}
+// Helper function to trim leading and trailing null characters from a string.
+static void trim_nulls(utility::string_t &str)
+{
+ size_t index;
+ for (index = 0; index < str.size() && str[index] == 0; ++index);
+ str.erase(0, index);
+ for (index = str.size(); index > 0 && str[index - 1] == 0; --index);
+ str.erase(index);
+}
+
// Helper function to get the reason phrase from a WinHTTP response.
static utility::string_t parse_reason_phrase(HINTERNET request_handle)
{
@@ -98,7 +109,7 @@ static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utf16char *he
response.set_status_code(parse_status_code(request_handle));
response.set_reason_phrase(parse_reason_phrase(request_handle));
- parse_headers_string(headersStr, response.headers());
+ web::http::details::parse_headers_string(headersStr, response.headers());
}
// Helper function to build error messages.
@@ -301,7 +312,13 @@ class winhttp_client : 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_hSession(nullptr), m_hConnection(nullptr) { }
+ : _http_client_communicator(std::move(address), std::move(client_config))
+ , m_secure(m_uri.scheme() == _XPLATSTR("https"))
+ , m_hSession(nullptr)
+ , m_hConnection(nullptr) { }
+
+ winhttp_client(const winhttp_client&) = delete;
+ winhttp_client &operator=(const winhttp_client&) = delete;
// Closes session.
~winhttp_client()
@@ -324,6 +341,20 @@ class winhttp_client : public _http_client_communicator
}
}
+ virtual pplx::task propagate(http_request request) override
+ {
+ auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this());
+ auto context = details::winhttp_request_context::create_request_context(self, request);
+
+ // Use a task to externally signal the final result and completion of the task.
+ auto result_task = pplx::create_task(context->m_request_completion);
+
+ // Asynchronously send the response with the HTTP client implementation.
+ this->async_send_request(context);
+
+ return result_task;
+ }
+
protected:
unsigned long report_failure(const utility::string_t& errorMessage)
@@ -568,7 +599,7 @@ class winhttp_client : public _http_client_communicator
{
if ( msg.method() == http::methods::GET || msg.method() == http::methods::HEAD )
{
- request->report_exception(http_exception(get_with_body));
+ request->report_exception(http_exception(get_with_body_err_msg));
return;
}
@@ -590,7 +621,7 @@ class winhttp_client : public _http_client_communicator
// Add headers.
if(!msg.headers().empty())
{
- const utility::string_t flattened_headers = flatten_http_headers(msg.headers());
+ const utility::string_t flattened_headers = web::http::details::flatten_http_headers(msg.headers());
if(!WinHttpAddRequestHeaders(
winhttp_context->m_request_handle,
flattened_headers.c_str(),
@@ -1261,28 +1292,11 @@ class winhttp_client : public _http_client_communicator
HINTERNET m_hSession;
HINTERNET m_hConnection;
bool m_secure;
-
- // No copy or assignment.
- winhttp_client(const winhttp_client&);
- winhttp_client &operator=(const winhttp_client&);
};
-http_network_handler::http_network_handler(const uri &base_uri, const http_client_config &client_config) :
- m_http_client_impl(std::make_shared(base_uri, client_config))
-{
-}
-
-pplx::task http_network_handler::propagate(http_request request)
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri base_uri, const http_client_config& client_config)
{
- auto context = details::winhttp_request_context::create_request_context(m_http_client_impl, request);
-
- // Use a task to externally signal the final result and completion of the task.
- auto result_task = pplx::create_task(context->m_request_completion);
-
- // Asynchronously send the response with the HTTP client implementation.
- m_http_client_impl->async_send_request(context);
-
- return result_task;
+ return std::make_shared(std::move(base_uri), client_config);
}
}}}}
diff --git a/Release/src/http/client/http_client_winrt.cpp b/Release/src/http/client/http_client_winrt.cpp
index d2b5a58cc4..5ae4a8ec52 100644
--- a/Release/src/http/client/http_client_winrt.cpp
+++ b/Release/src/http/client/http_client_winrt.cpp
@@ -26,7 +26,7 @@
****/
#include "stdafx.h"
-#include "cpprest/details/http_client_impl.h"
+#include "http_client_impl.h"
#include
// Important for WP8
@@ -109,7 +109,7 @@ class HttpRequestCallback :
}
}
- parse_headers_string(hdrStr, response.headers());
+ web::http::details::parse_headers_string(hdrStr, response.headers());
m_request->complete_headers();
return S_OK;
@@ -359,6 +359,23 @@ class winrt_client : public _http_client_communicator
winrt_client(http::uri address, http_client_config client_config)
: _http_client_communicator(std::move(address), std::move(client_config)) { }
+ winrt_client(const winrt_client&) = delete;
+ winrt_client &operator=(const winrt_client&) = delete;
+
+ virtual pplx::task propagate(http_request request) override
+ {
+ auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this());
+ auto context = details::winrt_request_context::create_request_context(self, request);
+
+ // Use a task to externally signal the final result and completion of the task.
+ auto result_task = pplx::create_task(context->m_request_completion);
+
+ // Asynchronously send the response with the HTTP client implementation.
+ this->async_send_request(context);
+
+ return result_task;
+ }
+
protected:
// Method to open client.
@@ -516,7 +533,7 @@ class winrt_client : public _http_client_communicator
{
if ( msg.method() == http::methods::GET || msg.method() == http::methods::HEAD )
{
- request->report_exception(http_exception(get_with_body));
+ request->report_exception(http_exception(get_with_body_err_msg));
return;
}
@@ -541,30 +558,11 @@ class winrt_client : public _http_client_communicator
});
}
}
-
-private:
-
- // No copy or assignment.
- winrt_client(const winrt_client&);
- winrt_client &operator=(const winrt_client&);
};
-http_network_handler::http_network_handler(const uri &base_uri, const http_client_config &client_config) :
- m_http_client_impl(std::make_shared(base_uri, client_config))
-{
-}
-
-pplx::task http_network_handler::propagate(http_request request)
+std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri base_uri, const http_client_config& client_config)
{
- auto context = details::winrt_request_context::create_request_context(m_http_client_impl, request);
-
- // Use a task to externally signal the final result and completion of the task.
- auto result_task = pplx::create_task(context->m_request_completion);
-
- // Asynchronously send the response with the HTTP client implementation.
- m_http_client_impl->async_send_request(context);
-
- return result_task;
+ return std::make_shared(std::move(base_uri), client_config);
}
}}}}
diff --git a/Release/src/http/client/x509_cert_utilities.cpp b/Release/src/http/client/x509_cert_utilities.cpp
index fed486dcc2..20eef9812f 100644
--- a/Release/src/http/client/x509_cert_utilities.cpp
+++ b/Release/src/http/client/x509_cert_utilities.cpp
@@ -25,11 +25,32 @@
#include "stdafx.h"
+#if defined(__APPLE__) || (defined(ANDROID) || defined(__ANDROID__)) || (defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(_M_ARM) && !defined(CPPREST_EXCLUDE_WEBSOCKETS))
+
#include "cpprest/details/x509_cert_utilities.h"
+#include
+
+#if defined(ANDROID) || defined(__ANDROID__)
+#include
+#endif
+
+#if defined(__APPLE__)
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#if defined(_WIN32)
+#include
+#include
+#endif
namespace web { namespace http { namespace client { namespace details {
-#if defined(__APPLE__) || (defined(ANDROID) || defined(__ANDROID__)) || (defined(_WIN32) && !defined(__cplusplus_winrt) && !defined(_M_ARM) && !defined(CPPREST_EXCLUDE_WEBSOCKETS))
+static bool verify_X509_cert_chain(const std::vector &certChain, const std::string &hostName);
+
bool verify_cert_chain_platform_specific(boost::asio::ssl::verify_context &verifyCtx, const std::string &hostName)
{
X509_STORE_CTX *storeContext = verifyCtx.native_handle();
@@ -83,6 +104,379 @@ bool verify_cert_chain_platform_specific(boost::asio::ssl::verify_context &verif
#endif
return verify_result;
}
+
+#if defined(ANDROID) || defined(__ANDROID__)
+using namespace crossplat;
+
+///
+/// Helper function to check return value and see if any exceptions
+/// occurred when calling a JNI function.
+///
+/// true if JNI call failed, false otherwise.
+static bool jni_failed(JNIEnv *env)
+{
+ if(env->ExceptionOccurred())
+ {
+ // Clear exception otherwise no other JNI functions can be called.
+ // In the future if we improve error reporting the exception message
+ // can be retrieved from here.
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+}
+template
+static bool jni_failed(JNIEnv *env, const java_local_ref &result)
+{
+ if(jni_failed(env) || !result)
+ {
+ return true;
+ }
+ return false;
+}
+static bool jni_failed(JNIEnv *env, const jmethodID &result)
+{
+ if(jni_failed(env) || result == nullptr)
+ {
+ return true;
+ }
+ return false;
+}
+#define CHECK_JREF(env, obj) if(jni_failed(env, obj)) return false;
+#define CHECK_JMID(env, mid) if(jni_failed(env, mid)) return false;
+#define CHECK_JNI(env) if(jni_failed(env)) return false;
+
+bool verify_X509_cert_chain(const std::vector &certChain, const std::string &hostName)
+{
+ JNIEnv* env = get_jvm_env();
+
+ // Possible performance improvement:
+ // In the future we could gain performance by turning all the jclass local
+ // references into global references. Then we could lazy initialize and
+ // save them globally. If this is done I'm not exactly sure where the release
+ // should be.
+
+ // ByteArrayInputStream
+ java_local_ref byteArrayInputStreamClass(env->FindClass("java/io/ByteArrayInputStream"));
+ CHECK_JREF(env, byteArrayInputStreamClass);
+ jmethodID byteArrayInputStreamConstructorMethod = env->GetMethodID(
+ byteArrayInputStreamClass.get(),
+ "",
+ "([B)V");
+ CHECK_JMID(env, byteArrayInputStreamConstructorMethod);
+
+ // CertificateFactory
+ java_local_ref certificateFactoryClass(env->FindClass("java/security/cert/CertificateFactory"));
+ CHECK_JREF(env, certificateFactoryClass);
+ jmethodID certificateFactoryGetInstanceMethod = env->GetStaticMethodID(
+ certificateFactoryClass.get(),
+ "getInstance",
+ "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
+ CHECK_JMID(env, certificateFactoryGetInstanceMethod);
+ jmethodID generateCertificateMethod = env->GetMethodID(
+ certificateFactoryClass.get(),
+ "generateCertificate",
+ "(Ljava/io/InputStream;)Ljava/security/cert/Certificate;");
+ CHECK_JMID(env, generateCertificateMethod);
+
+ // X509Certificate
+ java_local_ref X509CertificateClass(env->FindClass("java/security/cert/X509Certificate"));
+ CHECK_JREF(env, X509CertificateClass);
+
+ // TrustManagerFactory
+ java_local_ref trustManagerFactoryClass(env->FindClass("javax/net/ssl/TrustManagerFactory"));
+ CHECK_JREF(env, trustManagerFactoryClass);
+ jmethodID trustManagerFactoryGetInstanceMethod = env->GetStaticMethodID(
+ trustManagerFactoryClass.get(),
+ "getInstance",
+ "(Ljava/lang/String;)Ljavax/net/ssl/TrustManagerFactory;");
+ CHECK_JMID(env, trustManagerFactoryGetInstanceMethod);
+ jmethodID trustManagerFactoryInitMethod = env->GetMethodID(
+ trustManagerFactoryClass.get(),
+ "init",
+ "(Ljava/security/KeyStore;)V");
+ CHECK_JMID(env, trustManagerFactoryInitMethod);
+ jmethodID trustManagerFactoryGetTrustManagersMethod = env->GetMethodID(
+ trustManagerFactoryClass.get(),
+ "getTrustManagers",
+ "()[Ljavax/net/ssl/TrustManager;");
+ CHECK_JMID(env, trustManagerFactoryGetTrustManagersMethod);
+
+ // X509TrustManager
+ java_local_ref X509TrustManagerClass(env->FindClass("javax/net/ssl/X509TrustManager"));
+ CHECK_JREF(env, X509TrustManagerClass);
+ jmethodID X509TrustManagerCheckServerTrustedMethod = env->GetMethodID(
+ X509TrustManagerClass.get(),
+ "checkServerTrusted",
+ "([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V");
+ CHECK_JMID(env, X509TrustManagerCheckServerTrustedMethod);
+
+ // StrictHostnameVerifier
+ java_local_ref strictHostnameVerifierClass(env->FindClass("org/apache/http/conn/ssl/StrictHostnameVerifier"));
+ CHECK_JREF(env, strictHostnameVerifierClass);
+ jmethodID strictHostnameVerifierConstructorMethod = env->GetMethodID(strictHostnameVerifierClass.get(), "", "()V");
+ CHECK_JMID(env, strictHostnameVerifierConstructorMethod);
+ jmethodID strictHostnameVerifierVerifyMethod = env->GetMethodID(
+ strictHostnameVerifierClass.get(),
+ "verify",
+ "(Ljava/lang/String;Ljava/security/cert/X509Certificate;)V");
+ CHECK_JMID(env, strictHostnameVerifierVerifyMethod);
+
+ // Create CertificateFactory
+ java_local_ref XDot509String(env->NewStringUTF("X.509"));
+ CHECK_JREF(env, XDot509String);
+ java_local_ref certificateFactory(env->CallStaticObjectMethod(
+ certificateFactoryClass.get(),
+ certificateFactoryGetInstanceMethod,
+ XDot509String.get()));
+ CHECK_JREF(env, certificateFactory);
+
+ // Create Java array to store all the certs in.
+ java_local_ref certsArray(env->NewObjectArray(certChain.size(), X509CertificateClass.get(), nullptr));
+ CHECK_JREF(env, certsArray);
+
+ // For each certificate perform the following steps:
+ // 1. Create ByteArrayInputStream backed by DER certificate bytes
+ // 2. Create Certificate using CertificateFactory.generateCertificate
+ // 3. Add Certificate to array
+ int i = 0;
+ for(const auto &certData : certChain)
+ {
+ java_local_ref byteArray(env->NewByteArray(certData.size()));
+ CHECK_JREF(env, byteArray);
+ env->SetByteArrayRegion(byteArray.get(), 0, certData.size(), reinterpret_cast(certData.c_str()));
+ CHECK_JNI(env);
+ java_local_ref byteArrayInputStream(env->NewObject(
+ byteArrayInputStreamClass.get(),
+ byteArrayInputStreamConstructorMethod,
+ byteArray.get()));
+ CHECK_JREF(env, byteArrayInputStream);
+
+ java_local_ref cert(env->CallObjectMethod(
+ certificateFactory.get(),
+ generateCertificateMethod,
+ byteArrayInputStream.get()));
+ CHECK_JREF(env, cert);
+
+ env->SetObjectArrayElement(certsArray.get(), i, cert.get());
+ CHECK_JNI(env);
+ ++i;
+ }
+
+ // Create TrustManagerFactory, init with Android system certs
+ java_local_ref X509String(env->NewStringUTF("X509"));
+ CHECK_JREF(env, X509String);
+ java_local_ref trustFactoryManager(env->CallStaticObjectMethod(
+ trustManagerFactoryClass.get(),
+ trustManagerFactoryGetInstanceMethod,
+ X509String.get()));
+ CHECK_JREF(env, trustFactoryManager);
+ env->CallVoidMethod(trustFactoryManager.get(), trustManagerFactoryInitMethod, nullptr);
+ CHECK_JNI(env);
+
+ // Get TrustManager
+ java_local_ref trustManagerArray(static_cast(
+ env->CallObjectMethod(trustFactoryManager.get(), trustManagerFactoryGetTrustManagersMethod)));
+ CHECK_JREF(env, trustManagerArray);
+ java_local_ref trustManager(env->GetObjectArrayElement(trustManagerArray.get(), 0));
+ CHECK_JREF(env, trustManager);
+
+ // Validate certificate chain.
+ java_local_ref RSAString(env->NewStringUTF("RSA"));
+ CHECK_JREF(env, RSAString);
+ env->CallVoidMethod(
+ trustManager.get(),
+ X509TrustManagerCheckServerTrustedMethod,
+ certsArray.get(),
+ RSAString.get());
+ CHECK_JNI(env);
+
+ // Verify hostname on certificate according to RFC 2818.
+ java_local_ref hostnameVerifier(env->NewObject(
+ strictHostnameVerifierClass.get(), strictHostnameVerifierConstructorMethod));
+ CHECK_JREF(env, hostnameVerifier);
+ java_local_ref hostNameString(env->NewStringUTF(hostName.c_str()));
+ CHECK_JREF(env, hostNameString);
+ java_local_ref cert(env->GetObjectArrayElement(certsArray.get(), 0));
+ CHECK_JREF(env, cert);
+ env->CallVoidMethod(
+ hostnameVerifier.get(),
+ strictHostnameVerifierVerifyMethod,
+ hostNameString.get(),
+ cert.get());
+ CHECK_JNI(env);
+
+ return true;
+}
+#endif
+
+#if defined(__APPLE__)
+namespace {
+ // Simple RAII pattern wrapper to perform CFRelease on objects.
+ template
+ class cf_ref
+ {
+ public:
+ cf_ref(T v) : value(v)
+ {
+ static_assert(sizeof(cf_ref) == sizeof(T), "Code assumes just a wrapper, see usage in CFArrayCreate below.");
+ }
+ cf_ref() : value(nullptr) {}
+ cf_ref(cf_ref &&other) : value(other.value) { other.value = nullptr; }
+
+ ~cf_ref()
+ {
+ if(value != nullptr)
+ {
+ CFRelease(value);
+ }
+ }
+
+ T & get()
+ {
+ return value;
+ }
+ private:
+ cf_ref(const cf_ref &);
+ cf_ref & operator=(const cf_ref &);
+ T value;
+ };
+}
+
+bool verify_X509_cert_chain(const std::vector &certChain, const std::string &hostName)
+{
+ // Build up CFArrayRef with all the certificates.
+ // All this code is basically just to get into the correct structures for the Apple APIs.
+ // Copies are avoided whenever possible.
+ std::vector> certs;
+ for(const auto & certBuf : certChain)
+ {
+ cf_ref certDataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+ reinterpret_cast(certBuf.c_str()),
+ certBuf.size(),
+ kCFAllocatorNull);
+ if(certDataRef.get() == nullptr)
+ {
+ return false;
+ }
+
+ cf_ref certObj = SecCertificateCreateWithData(nullptr, certDataRef.get());
+ if(certObj.get() == nullptr)
+ {
+ return false;
+ }
+ certs.push_back(std::move(certObj));
+ }
+ cf_ref certsArray = CFArrayCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&certs[0])), certs.size(), nullptr);
+ if(certsArray.get() == nullptr)
+ {
+ return false;
+ }
+
+ // Create trust management object with certificates and SSL policy.
+ // Note: SecTrustCreateWithCertificates expects the certificate to be
+ // verified is the first element.
+ cf_ref cfHostName = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
+ hostName.c_str(),
+ kCFStringEncodingASCII,
+ kCFAllocatorNull);
+ if(cfHostName.get() == nullptr)
+ {
+ return false;
+ }
+ cf_ref policy = SecPolicyCreateSSL(true /* client side */, cfHostName.get());
+ cf_ref trust;
+ OSStatus status = SecTrustCreateWithCertificates(certsArray.get(), policy.get(), &trust.get());
+ if(status == noErr)
+ {
+ // Perform actual certificate verification.
+ SecTrustResultType trustResult;
+ status = SecTrustEvaluate(trust.get(), &trustResult);
+ if(status == noErr && (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+#endif
+
+#if defined(_WIN32)
+namespace {
+ // Helper RAII unique_ptrs to free Windows structures.
+ struct cert_free_certificate_context
+ {
+ void operator()(const CERT_CONTEXT *ctx) const
+ {
+ CertFreeCertificateContext(ctx);
+ }
+ };
+ typedef std::unique_ptr cert_context;
+ struct cert_free_certificate_chain
+ {
+ void operator()(const CERT_CHAIN_CONTEXT *chain) const
+ {
+ CertFreeCertificateChain(chain);
+ }
+ };
+ typedef std::unique_ptr chain_context;
+}
+
+bool verify_X509_cert_chain(const std::vector &certChain, const std::string &)
+{
+ // Create certificate context from server certificate.
+ cert_context cert(CertCreateCertificateContext(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ reinterpret_cast(certChain[0].c_str()),
+ static_cast(certChain[0].size())));
+ if (cert == nullptr)
+ {
+ return false;
+ }
+
+ // Let the OS build a certificate chain from the server certificate.
+ CERT_CHAIN_PARA params;
+ ZeroMemory(¶ms, sizeof(params));
+ params.cbSize = sizeof(CERT_CHAIN_PARA);
+ params.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ LPSTR usages [] =
+ {
+ szOID_PKIX_KP_SERVER_AUTH,
+
+ // For older servers and to match IE.
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ params.RequestedUsage.Usage.cUsageIdentifier = std::extent::value;
+ params.RequestedUsage.Usage.rgpszUsageIdentifier = usages;
+ PCCERT_CHAIN_CONTEXT chainContext;
+ chain_context chain;
+ if (!CertGetCertificateChain(
+ nullptr,
+ cert.get(),
+ nullptr,
+ nullptr,
+ ¶ms,
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN,
+ nullptr,
+ &chainContext))
+ {
+ return false;
+ }
+ chain.reset(chainContext);
+
+ // Check to see if the certificate chain is actually trusted.
+ if (chain->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR)
+ {
+ return false;
+ }
+
+ return true;
+}
#endif
+
}}}}
+
+#endif
diff --git a/Release/src/http/client/x509_cert_utilities_android.cpp b/Release/src/http/client/x509_cert_utilities_android.cpp
deleted file mode 100644
index 916582b222..0000000000
--- a/Release/src/http/client/x509_cert_utilities_android.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/***
-* ==++==
-*
-* Copyright (c) Microsoft Corporation. All rights reserved.
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-* ==--==
-* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-*
-* Contains utility functions for helping to verify server certificates on Android.
-*
-* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
-*
-* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-****/
-
-#include "stdafx.h"
-
-#include "cpprest/details/x509_cert_utilities.h"
-
-#include
-using namespace crossplat;
-
-namespace web { namespace http { namespace client { namespace details {
-
-///
-/// Helper function to check return value and see if any exceptions
-/// occurred when calling a JNI function.
-///
-/// true if JNI call failed, false otherwise.
-bool jni_failed(JNIEnv *env)
-{
- if(env->ExceptionOccurred())
- {
- // Clear exception otherwise no other JNI functions can be called.
- // In the future if we improve error reporting the exception message
- // can be retrieved from here.
- env->ExceptionClear();
- return true;
- }
- return false;
-}
-template
-bool jni_failed(JNIEnv *env, const java_local_ref &result)
-{
- if(jni_failed(env) || !result)
- {
- return true;
- }
- return false;
-}
-bool jni_failed(JNIEnv *env, const jmethodID &result)
-{
- if(jni_failed(env) || result == nullptr)
- {
- return true;
- }
- return false;
-}
-#define CHECK_JREF(env, obj) if(jni_failed(env, obj)) return false;
-#define CHECK_JMID(env, mid) if(jni_failed(env, mid)) return false;
-#define CHECK_JNI(env) if(jni_failed(env)) return false;
-
-bool verify_X509_cert_chain(const std::vector &certChain, const std::string &hostName)
-{
- JNIEnv* env = get_jvm_env();
-
- // Possible performance improvement:
- // In the future we could gain performance by turning all the jclass local
- // references into global references. Then we could lazy initialize and
- // save them globally. If this is done I'm not exactly sure where the release
- // should be.
-
- // ByteArrayInputStream
- java_local_ref byteArrayInputStreamClass(env->FindClass("java/io/ByteArrayInputStream"));
- CHECK_JREF(env, byteArrayInputStreamClass);
- jmethodID byteArrayInputStreamConstructorMethod = env->GetMethodID(
- byteArrayInputStreamClass.get(),
- "",
- "([B)V");
- CHECK_JMID(env, byteArrayInputStreamConstructorMethod);
-
- // CertificateFactory
- java_local_ref certificateFactoryClass(env->FindClass("java/security/cert/CertificateFactory"));
- CHECK_JREF(env, certificateFactoryClass);
- jmethodID certificateFactoryGetInstanceMethod = env->GetStaticMethodID(
- certificateFactoryClass.get(),
- "getInstance",
- "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
- CHECK_JMID(env, certificateFactoryGetInstanceMethod);
- jmethodID generateCertificateMethod = env->GetMethodID(
- certificateFactoryClass.get(),
- "generateCertificate",
- "(Ljava/io/InputStream;)Ljava/security/cert/Certificate;");
- CHECK_JMID(env, generateCertificateMethod);
-
- // X509Certificate
- java_local_ref X509CertificateClass(env->FindClass("java/security/cert/X509Certificate"));
- CHECK_JREF(env, X509CertificateClass);
-
- // TrustManagerFactory
- java_local_ref trustManagerFactoryClass(env->FindClass("javax/net/ssl/TrustManagerFactory"));
- CHECK_JREF(env, trustManagerFactoryClass);
- jmethodID trustManagerFactoryGetInstanceMethod = env->GetStaticMethodID(
- trustManagerFactoryClass.get(),
- "getInstance",
- "(Ljava/lang/String;)Ljavax/net/ssl/TrustManagerFactory;");
- CHECK_JMID(env, trustManagerFactoryGetInstanceMethod);
- jmethodID trustManagerFactoryInitMethod = env->GetMethodID(
- trustManagerFactoryClass.get(),
- "init",
- "(Ljava/security/KeyStore;)V");
- CHECK_JMID(env, trustManagerFactoryInitMethod);
- jmethodID trustManagerFactoryGetTrustManagersMethod = env->GetMethodID(
- trustManagerFactoryClass.get(),
- "getTrustManagers",
- "()[Ljavax/net/ssl/TrustManager;");
- CHECK_JMID(env, trustManagerFactoryGetTrustManagersMethod);
-
- // X509TrustManager
- java_local_ref X509TrustManagerClass(env->FindClass("javax/net/ssl/X509TrustManager"));
- CHECK_JREF(env, X509TrustManagerClass);
- jmethodID X509TrustManagerCheckServerTrustedMethod = env->GetMethodID(
- X509TrustManagerClass.get(),
- "checkServerTrusted",
- "([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V");
- CHECK_JMID(env, X509TrustManagerCheckServerTrustedMethod);
-
- // StrictHostnameVerifier
- java_local_ref strictHostnameVerifierClass(env->FindClass("org/apache/http/conn/ssl/StrictHostnameVerifier"));
- CHECK_JREF(env, strictHostnameVerifierClass);
- jmethodID strictHostnameVerifierConstructorMethod = env->GetMethodID(strictHostnameVerifierClass.get(), "", "()V");
- CHECK_JMID(env, strictHostnameVerifierConstructorMethod);
- jmethodID strictHostnameVerifierVerifyMethod = env->GetMethodID(
- strictHostnameVerifierClass.get(),
- "verify",
- "(Ljava/lang/String;Ljava/security/cert/X509Certificate;)V");
- CHECK_JMID(env, strictHostnameVerifierVerifyMethod);
-
- // Create CertificateFactory
- java_local_ref XDot509String(env->NewStringUTF("X.509"));
- CHECK_JREF(env, XDot509String);
- java_local_ref certificateFactory(env->CallStaticObjectMethod(
- certificateFactoryClass.get(),
- certificateFactoryGetInstanceMethod,
- XDot509String.get()));
- CHECK_JREF(env, certificateFactory);
-
- // Create Java array to store all the certs in.
- java_local_ref certsArray(env->NewObjectArray(certChain.size(), X509CertificateClass.get(), nullptr));
- CHECK_JREF(env, certsArray);
-
- // For each certificate perform the following steps:
- // 1. Create ByteArrayInputStream backed by DER certificate bytes
- // 2. Create Certificate using CertificateFactory.generateCertificate
- // 3. Add Certificate to array
- int i = 0;
- for(const auto &certData : certChain)
- {
- java_local_ref byteArray(env->NewByteArray(certData.size()));
- CHECK_JREF(env, byteArray);
- env->SetByteArrayRegion(byteArray.get(), 0, certData.size(), reinterpret_cast(certData.c_str()));
- CHECK_JNI(env);
- java_local_ref byteArrayInputStream(env->NewObject(
- byteArrayInputStreamClass.get(),
- byteArrayInputStreamConstructorMethod,
- byteArray.get()));
- CHECK_JREF(env, byteArrayInputStream);
-
- java_local_ref cert(env->CallObjectMethod(
- certificateFactory.get(),
- generateCertificateMethod,
- byteArrayInputStream.get()));
- CHECK_JREF(env, cert);
-
- env->SetObjectArrayElement(certsArray.get(), i, cert.get());
- CHECK_JNI(env);
- ++i;
- }
-
- // Create TrustManagerFactory, init with Android system certs
- java_local_ref X509String(env->NewStringUTF("X509"));
- CHECK_JREF(env, X509String);
- java_local_ref trustFactoryManager(env->CallStaticObjectMethod(
- trustManagerFactoryClass.get(),
- trustManagerFactoryGetInstanceMethod,
- X509String.get()));
- CHECK_JREF(env, trustFactoryManager);
- env->CallVoidMethod(trustFactoryManager.get(), trustManagerFactoryInitMethod, nullptr);
- CHECK_JNI(env);
-
- // Get TrustManager
- java_local_ref trustManagerArray(static_cast(
- env->CallObjectMethod(trustFactoryManager.get(), trustManagerFactoryGetTrustManagersMethod)));
- CHECK_JREF(env, trustManagerArray);
- java_local_ref trustManager(env->GetObjectArrayElement(trustManagerArray.get(), 0));
- CHECK_JREF(env, trustManager);
-
- // Validate certificate chain.
- java_local_ref RSAString(env->NewStringUTF("RSA"));
- CHECK_JREF(env, RSAString);
- env->CallVoidMethod(
- trustManager.get(),
- X509TrustManagerCheckServerTrustedMethod,
- certsArray.get(),
- RSAString.get());
- CHECK_JNI(env);
-
- // Verify hostname on certificate according to RFC 2818.
- java_local_ref hostnameVerifier(env->NewObject(
- strictHostnameVerifierClass.get(), strictHostnameVerifierConstructorMethod));
- CHECK_JREF(env, hostnameVerifier);
- java_local_ref hostNameString(env->NewStringUTF(hostName.c_str()));
- CHECK_JREF(env, hostNameString);
- java_local_ref cert(env->GetObjectArrayElement(certsArray.get(), 0));
- CHECK_JREF(env, cert);
- env->CallVoidMethod(
- hostnameVerifier.get(),
- strictHostnameVerifierVerifyMethod,
- hostNameString.get(),
- cert.get());
- CHECK_JNI(env);
-
- return true;
-}
-
-}}}}
\ No newline at end of file
diff --git a/Release/src/http/client/x509_cert_utilities_apple.cpp b/Release/src/http/client/x509_cert_utilities_apple.cpp
deleted file mode 100644
index c311144d56..0000000000
--- a/Release/src/http/client/x509_cert_utilities_apple.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/***
-* ==++==
-*
-* Copyright (c) Microsoft Corporation. All rights reserved.
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-* ==--==
-* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-*
-* Contains utility functions for helping to verify server certificates on OSX/iOS.
-*
-* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
-*
-* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-****/
-
-#include "stdafx.h"
-
-#include "cpprest/details/x509_cert_utilities.h"
-
-#include
-#include
-#include
-#include
-#include
-
-namespace web { namespace http { namespace client { namespace details {
-
-// Simple RAII pattern wrapper to perform CFRelease on objects.
-template
-class cf_ref
-{
-public:
- cf_ref(T v) : value(v)
- {
- static_assert(sizeof(cf_ref) == sizeof(T), "Code assumes just a wrapper, see usage in CFArrayCreate below.");
- }
- cf_ref() : value(nullptr) {}
- cf_ref(cf_ref &&other) : value(other.value) { other.value = nullptr; }
-
- ~cf_ref()
- {
- if(value != nullptr)
- {
- CFRelease(value);
- }
- }
-
- T & get()
- {
- return value;
- }
-private:
- cf_ref(const cf_ref &);
- cf_ref & operator=(const cf_ref &);
- T value;
-};
-
-bool verify_X509_cert_chain(const std::vector &certChain, const std::string &hostName)
-{
- // Build up CFArrayRef with all the certificates.
- // All this code is basically just to get into the correct structures for the Apple APIs.
- // Copies are avoided whenever possible.
- std::vector> certs;
- for(const auto & certBuf : certChain)
- {
- cf_ref certDataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
- reinterpret_cast(certBuf.c_str()),
- certBuf.size(),
- kCFAllocatorNull);
- if(certDataRef.get() == nullptr)
- {
- return false;
- }
-
- cf_ref certObj = SecCertificateCreateWithData(nullptr, certDataRef.get());
- if(certObj.get() == nullptr)
- {
- return false;
- }
- certs.push_back(std::move(certObj));
- }
- cf_ref certsArray = CFArrayCreate(kCFAllocatorDefault, const_cast(reinterpret_cast(&certs[0])), certs.size(), nullptr);
- if(certsArray.get() == nullptr)
- {
- return false;
- }
-
- // Create trust management object with certificates and SSL policy.
- // Note: SecTrustCreateWithCertificates expects the certificate to be
- // verified is the first element.
- cf_ref cfHostName = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
- hostName.c_str(),
- kCFStringEncodingASCII,
- kCFAllocatorNull);
- if(cfHostName.get() == nullptr)
- {
- return false;
- }
- cf_ref policy = SecPolicyCreateSSL(true /* client side */, cfHostName.get());
- cf_ref trust;
- OSStatus status = SecTrustCreateWithCertificates(certsArray.get(), policy.get(), &trust.get());
- if(status == noErr)
- {
- // Perform actual certificate verification.
- SecTrustResultType trustResult;
- status = SecTrustEvaluate(trust.get(), &trustResult);
- if(status == noErr && (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed))
- {
- return true;
- }
- }
-
- return false;
-}
-
-}}}}
\ No newline at end of file
diff --git a/Release/src/http/client/x509_cert_utilities_win32.cpp b/Release/src/http/client/x509_cert_utilities_win32.cpp
deleted file mode 100644
index cd2b671424..0000000000
--- a/Release/src/http/client/x509_cert_utilities_win32.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/***
-* ==++==
-*
-* Copyright (c) Microsoft Corporation. All rights reserved.
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-* ==--==
-* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
-*
-* Contains utility functions for helping to verify server certificates on Windows desktop.
-*
-* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
-*
-* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-****/
-
-#include "stdafx.h"
-
-#include "cpprest/details/x509_cert_utilities.h"
-
-#include
-#include
-
-namespace web { namespace http { namespace client { namespace details {
-
-// Helper RAII unique_ptrs to free Windows structures.
-struct cert_free_certificate_context
-{
- void operator()(const CERT_CONTEXT *ctx) const
- {
- CertFreeCertificateContext(ctx);
- }
-};
-typedef std::unique_ptr cert_context;
-struct cert_free_certificate_chain
-{
- void operator()(const CERT_CHAIN_CONTEXT *chain) const
- {
- CertFreeCertificateChain(chain);
- }
-};
-typedef std::unique_ptr chain_context;
-
-bool verify_X509_cert_chain(const std::vector &certChain, const std::string &)
-{
- // Create certificate context from server certificate.
- cert_context cert(CertCreateCertificateContext(
- X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
- reinterpret_cast(certChain[0].c_str()),
- static_cast(certChain[0].size())));
- if (cert == nullptr)
- {
- return false;
- }
-
- // Let the OS build a certificate chain from the server certificate.
- CERT_CHAIN_PARA params;
- ZeroMemory(¶ms, sizeof(params));
- params.cbSize = sizeof(CERT_CHAIN_PARA);
- params.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
- LPSTR usages [] =
- {
- szOID_PKIX_KP_SERVER_AUTH,
-
- // For older servers and to match IE.
- szOID_SERVER_GATED_CRYPTO,
- szOID_SGC_NETSCAPE
- };
- params.RequestedUsage.Usage.cUsageIdentifier = std::extent::value;
- params.RequestedUsage.Usage.rgpszUsageIdentifier = usages;
- PCCERT_CHAIN_CONTEXT chainContext;
- chain_context chain;
- if (!CertGetCertificateChain(
- nullptr,
- cert.get(),
- nullptr,
- nullptr,
- ¶ms,
- CERT_CHAIN_REVOCATION_CHECK_CHAIN,
- nullptr,
- &chainContext))
- {
- return false;
- }
- chain.reset(chainContext);
-
- // Check to see if the certificate chain is actually trusted.
- if (chain->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR)
- {
- return false;
- }
-
- return true;
-}
-
-}}}}
\ No newline at end of file
diff --git a/Release/src/http/common/http_helpers.cpp b/Release/src/http/common/http_helpers.cpp
index 1424dbe382..0d60c04ea9 100644
--- a/Release/src/http/common/http_helpers.cpp
+++ b/Release/src/http/common/http_helpers.cpp
@@ -34,145 +34,6 @@ namespace web { namespace http
namespace details
{
-bool is_content_type_one_of(const utility::string_t *first, const utility::string_t *last, const utility::string_t &value)
-{
- while (first != last)
- {
- if (utility::details::str_icmp(*first, value))
- {
- return true;
- }
- ++first;
- }
- return false;
-}
-
-// Remove once VS 2013 is no longer supported.
-#if defined(_WIN32) && _MSC_VER < 1900
-// Not referring to mime_types to avoid static initialization order fiasco.
-static const utility::string_t textual_types [] = {
- U("message/http"),
- U("application/json"),
- U("application/xml"),
- U("application/atom+xml"),
- U("application/http"),
- U("application/x-www-form-urlencoded")
-};
-#endif
-bool is_content_type_textual(const utility::string_t &content_type)
-{
-#if !defined(_WIN32) || _MSC_VER >= 1900
- static const utility::string_t textual_types [] = {
- mime_types::message_http,
- mime_types::application_json,
- mime_types::application_xml,
- mime_types::application_atom_xml,
- mime_types::application_http,
- mime_types::application_x_www_form_urlencoded
- };
-#endif
-
- if (content_type.size() >= 4 && utility::details::str_icmp(content_type.substr(0, 4), _XPLATSTR("text")))
- {
- return true;
- }
- return (is_content_type_one_of(std::begin(textual_types), std::end(textual_types), content_type));
-}
-
-// Remove once VS 2013 is no longer supported.
-#if defined(_WIN32) && _MSC_VER < 1900
-// Not referring to mime_types to avoid static initialization order fiasco.
-static const utility::string_t json_types [] = {
- U("application/json"),
- U("application/x-json"),
- U("text/json"),
- U("text/x-json"),
- U("text/javascript"),
- U("text/x-javascript"),
- U("application/javascript"),
- U("application/x-javascript")
-};
-#endif
-bool is_content_type_json(const utility::string_t &content_type)
-{
-#if !defined(_WIN32) || _MSC_VER >= 1900
- static const utility::string_t json_types [] = {
- mime_types::application_json,
- mime_types::application_xjson,
- mime_types::text_json,
- mime_types::text_xjson,
- mime_types::text_javascript,
- mime_types::text_xjavascript,
- mime_types::application_javascript,
- mime_types::application_xjavascript
- };
-#endif
-
- return (is_content_type_one_of(std::begin(json_types), std::end(json_types), content_type));
-}
-
-void parse_content_type_and_charset(const utility::string_t &content_type, utility::string_t &content, utility::string_t &charset)
-{
- const size_t semi_colon_index = content_type.find_first_of(_XPLATSTR(";"));
-
- // No charset specified.
- if (semi_colon_index == utility::string_t::npos)
- {
- content = content_type;
- trim_whitespace(content);
- charset = get_default_charset(content);
- return;
- }
-
- // Split into content type and second part which could be charset.
- content = content_type.substr(0, semi_colon_index);
- trim_whitespace(content);
- utility::string_t possible_charset = content_type.substr(semi_colon_index + 1);
- trim_whitespace(possible_charset);
- const size_t equals_index = possible_charset.find_first_of(_XPLATSTR("="));
-
- // No charset specified.
- if (equals_index == utility::string_t::npos)
- {
- charset = get_default_charset(content);
- return;
- }
-
- // Split and make sure 'charset'
- utility::string_t charset_key = possible_charset.substr(0, equals_index);
- trim_whitespace(charset_key);
- if (!utility::details::str_icmp(charset_key, _XPLATSTR("charset")))
- {
- charset = get_default_charset(content);
- return;
- }
- charset = possible_charset.substr(equals_index + 1);
- // Remove the redundant ';' at the end of charset.
- while (charset.back() == ';')
- {
- charset.pop_back();
- }
- trim_whitespace(charset);
- if (charset.front() == _XPLATSTR('"') && charset.back() == _XPLATSTR('"'))
- {
- charset = charset.substr(1, charset.size() - 2);
- trim_whitespace(charset);
- }
-}
-
-utility::string_t get_default_charset(const utility::string_t &content_type)
-{
- // We are defaulting everything to Latin1 except JSON which is utf-8.
- if (is_content_type_json(content_type))
- {
- return charset_types::utf8;
- }
- else
- {
- return charset_types::latin1;
- }
-}
-
// Remove once VS 2013 is no longer supported.
#if defined(_WIN32) && _MSC_VER < 1900
static const http_status_to_phrase idToPhraseMap [] = {
@@ -210,149 +71,13 @@ utility::string_t get_default_reason_phrase(status_code code)
return phrase;
}
-// Helper function to determine byte order mark.
-enum endian_ness
-{
- little_endian,
- big_endian,
- unknown
-};
-static endian_ness check_byte_order_mark(const utf16string &str)
-{
- if (str.empty())
- {
- return unknown;
- }
- const unsigned char *src = (const unsigned char *) &str[0];
-
- // little endian
- if (src[0] == 0xFF && src[1] == 0xFE)
- {
- return little_endian;
- }
-
- // big endian
- else if (src[0] == 0xFE && src[1] == 0xFF)
- {
- return big_endian;
- }
-
- return unknown;
-}
-
-utility::string_t convert_utf16_to_string_t(utf16string src)
-{
-#ifdef _UTF16_STRINGS
- return convert_utf16_to_utf16(std::move(src));
-#else
- return convert_utf16_to_utf8(std::move(src));
-#endif
-}
-
-std::string convert_utf16_to_utf8(utf16string src)
-{
- const endian_ness endian = check_byte_order_mark(src);
- switch (endian)
- {
- case little_endian:
- return convert_utf16le_to_utf8(std::move(src), true);
- case big_endian:
- return convert_utf16be_to_utf8(std::move(src), true);
- case unknown:
- // unknown defaults to big endian.
- return convert_utf16be_to_utf8(std::move(src), false);
- }
- __assume(0);
-}
-
-utf16string convert_utf16_to_utf16(utf16string src)
-{
- const endian_ness endian = check_byte_order_mark(src);
- switch (endian)
- {
- case little_endian:
- src.erase(0, 1);
- return src;
- case big_endian:
- return convert_utf16be_to_utf16le(std::move(src), true);
- case unknown:
- // unknown defaults to big endian.
- return convert_utf16be_to_utf16le(std::move(src), false);
- }
- __assume(0);
-}
-
-std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom)
-{
- if (erase_bom && !src.empty())
- {
- src.erase(0, 1);
- }
- return utf16_to_utf8(std::move(src));
-}
-
-utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom)
-{
- if (erase_bom && !src.empty())
- {
- src.erase(0, 1);
- }
-#ifdef _UTF16_STRINGS
- return std::move(src);
-#else
- return utf16_to_utf8(std::move(src));
-#endif
-}
-
-// Helper function to change endian ness from big endian to little endian
-static utf16string big_endian_to_little_endian(utf16string src, bool erase_bom)
-{
- if (erase_bom && !src.empty())
- {
- src.erase(0, 1);
- }
- if (src.empty())
- {
- return src;
- }
-
- const size_t size = src.size();
- for (size_t i = 0; i < size; ++i)
- {
- utf16char ch = src[i];
- src[i] = static_cast(ch << 8);
- src[i] = static_cast(src[i] | ch >> 8);
- }
-
- return src;
-}
-
-utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom)
-{
-#ifdef _UTF16_STRINGS
- return convert_utf16be_to_utf16le(std::move(src), erase_bom);
-#else
- return convert_utf16be_to_utf8(std::move(src), erase_bom);
-#endif
-}
-
-std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom)
-{
- return utf16_to_utf8(big_endian_to_little_endian(std::move(src), erase_bom));
-}
-
-utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom)
-{
- return big_endian_to_little_endian(std::move(src), erase_bom);
-}
-
-void ltrim_whitespace(utility::string_t &str)
+static void ltrim_whitespace(utility::string_t &str)
{
size_t index;
for (index = 0; index < str.size() && isspace(str[index]); ++index);
str.erase(0, index);
}
-void rtrim_whitespace(utility::string_t &str)
+static void rtrim_whitespace(utility::string_t &str)
{
size_t index;
for (index = str.size(); index > 0 && isspace(str[index - 1]); --index);
diff --git a/Release/src/http/common/http_msg.cpp b/Release/src/http/common/http_msg.cpp
index c655f7a989..ba236ce8d3 100644
--- a/Release/src/http/common/http_msg.cpp
+++ b/Release/src/http/common/http_msg.cpp
@@ -42,6 +42,146 @@ utility::string_t http_headers::content_type() const
return result;
}
+
+
+/// Helper functions to convert a series of bytes from a charset to utf-8 or utf-16.
+/// These APIs deal with checking for and handling byte order marker (BOM).
+namespace {
+ enum endianness
+ {
+ little_endian,
+ big_endian,
+ unknown
+ };
+ endianness check_byte_order_mark(const utf16string &str)
+ {
+ if (str.empty())
+ {
+ return unknown;
+ }
+ const unsigned char *src = reinterpret_cast(str.data());
+
+ // little endian
+ if (src[0] == 0xFF && src[1] == 0xFE)
+ {
+ return little_endian;
+ }
+
+ // big endian
+ else if (src[0] == 0xFE && src[1] == 0xFF)
+ {
+ return big_endian;
+ }
+
+ return unknown;
+ }
+
+ std::string convert_utf16le_to_utf8(utf16string src, bool erase_bom)
+ {
+ if (erase_bom && !src.empty())
+ {
+ src.erase(0, 1);
+ }
+ return utf16_to_utf8(std::move(src));
+ }
+
+ utility::string_t convert_utf16le_to_string_t(utf16string src, bool erase_bom)
+ {
+ if (erase_bom && !src.empty())
+ {
+ src.erase(0, 1);
+ }
+ #ifdef _UTF16_STRINGS
+ return src;
+ #else
+ return utf16_to_utf8(std::move(src));
+ #endif
+ }
+
+ // Helper function to change endian ness from big endian to little endian
+ utf16string big_endian_to_little_endian(utf16string src, bool erase_bom)
+ {
+ if (erase_bom && !src.empty())
+ {
+ src.erase(0, 1);
+ }
+ if (src.empty())
+ {
+ return src;
+ }
+
+ const size_t size = src.size();
+ for (size_t i = 0; i < size; ++i)
+ {
+ utf16char ch = src[i];
+ src[i] = static_cast(ch << 8);
+ src[i] = static_cast(src[i] | ch >> 8);
+ }
+
+ return src;
+ }
+
+ std::string convert_utf16be_to_utf8(utf16string src, bool erase_bom)
+ {
+ return utf16_to_utf8(big_endian_to_little_endian(std::move(src), erase_bom));
+ }
+
+ utf16string convert_utf16be_to_utf16le(utf16string src, bool erase_bom)
+ {
+ return big_endian_to_little_endian(std::move(src), erase_bom);
+ }
+
+ utility::string_t convert_utf16be_to_string_t(utf16string src, bool erase_bom)
+ {
+ #ifdef _UTF16_STRINGS
+ return convert_utf16be_to_utf16le(std::move(src), erase_bom);
+ #else
+ return convert_utf16be_to_utf8(std::move(src), erase_bom);
+ #endif
+ }
+
+ std::string convert_utf16_to_utf8(utf16string src)
+ {
+ const endianness endian = check_byte_order_mark(src);
+ switch (endian)
+ {
+ case little_endian:
+ return convert_utf16le_to_utf8(std::move(src), true);
+ case big_endian:
+ return convert_utf16be_to_utf8(std::move(src), true);
+ case unknown:
+ // unknown defaults to big endian.
+ return convert_utf16be_to_utf8(std::move(src), false);
+ }
+ __assume(0);
+ }
+
+ utf16string convert_utf16_to_utf16(utf16string src)
+ {
+ const endianness endian = check_byte_order_mark(src);
+ switch (endian)
+ {
+ case little_endian:
+ src.erase(0, 1);
+ return src;
+ case big_endian:
+ return convert_utf16be_to_utf16le(std::move(src), true);
+ case unknown:
+ // unknown defaults to big endian.
+ return convert_utf16be_to_utf16le(std::move(src), false);
+ }
+ __assume(0);
+ }
+ utility::string_t convert_utf16_to_string_t(utf16string src)
+ {
+ #ifdef _UTF16_STRINGS
+ return convert_utf16_to_utf16(std::move(src));
+ #else
+ return convert_utf16_to_utf8(std::move(src));
+ #endif
+ }
+}
+
void http_headers::set_content_type(utility::string_t type)
{
m_headers[http::header_names::content_type] = std::move(type);
@@ -83,6 +223,45 @@ void http_headers::set_content_length(utility::size64_t length)
m_headers[http::header_names::content_length] = utility::conversions::print_string(length, std::locale::classic());
}
+namespace details {
+
+utility::string_t flatten_http_headers(const http_headers &headers)
+{
+ utility::string_t flattened_headers;
+ for (auto iter = headers.begin(); iter != headers.end(); ++iter)
+ {
+ flattened_headers.append(iter->first);
+ flattened_headers.push_back(':');
+ flattened_headers.append(iter->second);
+ flattened_headers.append(CRLF);
+ }
+ return flattened_headers;
+}
+
+#if defined(_WIN32)
+void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers)
+{
+ utf16char *context = nullptr;
+ utf16char *line = wcstok_s(headersStr, CRLF, &context);
+ while (line != nullptr)
+ {
+ const utility::string_t header_line(line);
+ 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);
+ headers.add(key, value);
+ }
+ line = wcstok_s(nullptr, CRLF, &context);
+ }
+}
+#endif
+
+}
+
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.");
@@ -218,13 +397,168 @@ void http_msg_base::_complete(utility::size64_t body_size, const std::exception_
}
}
+static bool is_content_type_one_of(const utility::string_t *first, const utility::string_t *last, const utility::string_t &value)
+{
+ while (first != last)
+ {
+ if (utility::details::str_icmp(*first, value))
+ {
+ return true;
+ }
+ ++first;
+ }
+ return false;
+}
+
+// Remove once VS 2013 is no longer supported.
+#if defined(_WIN32) && _MSC_VER < 1900
+// Not referring to mime_types to avoid static initialization order fiasco.
+static const utility::string_t textual_types [] = {
+ U("message/http"),
+ U("application/json"),
+ U("application/xml"),
+ U("application/atom+xml"),
+ U("application/http"),
+ U("application/x-www-form-urlencoded")
+};
+#endif
+
+///
+/// Determines whether or not the given content type is 'textual' according the feature specifications.
+///
+static bool is_content_type_textual(const utility::string_t &content_type)
+{
+#if !defined(_WIN32) || _MSC_VER >= 1900
+ static const utility::string_t textual_types [] = {
+ mime_types::message_http,
+ mime_types::application_json,
+ mime_types::application_xml,
+ mime_types::application_atom_xml,
+ mime_types::application_http,
+ mime_types::application_x_www_form_urlencoded
+ };
+#endif
+
+ if (content_type.size() >= 4 && utility::details::str_icmp(content_type.substr(0, 4), _XPLATSTR("text")))
+ {
+ return true;
+ }
+ return (is_content_type_one_of(std::begin(textual_types), std::end(textual_types), content_type));
+}
+
+// Remove once VS 2013 is no longer supported.
+#if defined(_WIN32) && _MSC_VER < 1900
+// Not referring to mime_types to avoid static initialization order fiasco.
+static const utility::string_t json_types [] = {
+ U("application/json"),
+ U("application/x-json"),
+ U("text/json"),
+ U("text/x-json"),
+ U("text/javascript"),
+ U("text/x-javascript"),
+ U("application/javascript"),
+ U("application/x-javascript")
+};
+#endif
+
+///
+/// Determines whether or not the given content type is JSON according the feature specifications.
+///
+static bool is_content_type_json(const utility::string_t &content_type)
+{
+#if !defined(_WIN32) || _MSC_VER >= 1900
+ static const utility::string_t json_types [] = {
+ mime_types::application_json,
+ mime_types::application_xjson,
+ mime_types::text_json,
+ mime_types::text_xjson,
+ mime_types::text_javascript,
+ mime_types::text_xjavascript,
+ mime_types::application_javascript,
+ mime_types::application_xjavascript
+ };
+#endif
+
+ return (is_content_type_one_of(std::begin(json_types), std::end(json_types), content_type));
+}
+
+///
+/// Gets the default charset for given content type. If the MIME type is not textual or recognized Latin1 will be returned.
+///
+static utility::string_t get_default_charset(const utility::string_t &content_type)
+{
+ // We are defaulting everything to Latin1 except JSON which is utf-8.
+ if (is_content_type_json(content_type))
+ {
+ return charset_types::utf8;
+ }
+ else
+ {
+ return charset_types::latin1;
+ }
+}
+
+
+///
+/// Parses the given Content-Type header value to get out actual content type and charset.
+/// If the charset isn't specified the default charset for the content type will be set.
+///
+static void parse_content_type_and_charset(const utility::string_t &content_type, utility::string_t &content, utility::string_t &charset)
+{
+ const size_t semi_colon_index = content_type.find_first_of(_XPLATSTR(";"));
+
+ // No charset specified.
+ if (semi_colon_index == utility::string_t::npos)
+ {
+ content = content_type;
+ trim_whitespace(content);
+ charset = get_default_charset(content);
+ return;
+ }
+
+ // Split into content type and second part which could be charset.
+ content = content_type.substr(0, semi_colon_index);
+ trim_whitespace(content);
+ utility::string_t possible_charset = content_type.substr(semi_colon_index + 1);
+ trim_whitespace(possible_charset);
+ const size_t equals_index = possible_charset.find_first_of(_XPLATSTR("="));
+
+ // No charset specified.
+ if (equals_index == utility::string_t::npos)
+ {
+ charset = get_default_charset(content);
+ return;
+ }
+
+ // Split and make sure 'charset'
+ utility::string_t charset_key = possible_charset.substr(0, equals_index);
+ trim_whitespace(charset_key);
+ if (!utility::details::str_icmp(charset_key, _XPLATSTR("charset")))
+ {
+ charset = get_default_charset(content);
+ return;
+ }
+ charset = possible_charset.substr(equals_index + 1);
+ // Remove the redundant ';' at the end of charset.
+ while (charset.back() == ';')
+ {
+ charset.pop_back();
+ }
+ trim_whitespace(charset);
+ if (charset.front() == _XPLATSTR('"') && charset.back() == _XPLATSTR('"'))
+ {
+ charset = charset.substr(1, charset.size() - 2);
+ trim_whitespace(charset);
+ }
+}
+
utility::string_t details::http_msg_base::parse_and_check_content_type(bool ignore_content_type, const std::function &check_content_type)
{
if (!instream())
{
throw http_exception(stream_was_set_explicitly);
}
-
+
utility::string_t content, charset = charset_types::utf8;
if (!ignore_content_type)
{
diff --git a/Release/src/websockets/client/ws_client_wspp.cpp b/Release/src/websockets/client/ws_client_wspp.cpp
index e8f5d2edbe..81512bc89f 100644
--- a/Release/src/websockets/client/ws_client_wspp.cpp
+++ b/Release/src/websockets/client/ws_client_wspp.cpp
@@ -24,10 +24,11 @@
****/
#include "stdafx.h"
-#include "cpprest/details/x509_cert_utilities.h"
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
+#include "cpprest/details/x509_cert_utilities.h"
+
// Force websocketpp to use C++ std::error_code instead of Boost.
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#if defined(__GNUC__)
diff --git a/Release/tests/functional/http/client/pipeline_stage_tests.cpp b/Release/tests/functional/http/client/pipeline_stage_tests.cpp
index 4e166e26f7..e276a013b2 100644
--- a/Release/tests/functional/http/client/pipeline_stage_tests.cpp
+++ b/Release/tests/functional/http/client/pipeline_stage_tests.cpp
@@ -244,7 +244,7 @@ class modify_count_responses_stage : public http_pipeline_stage
{
request.headers().set_content_type(U("modified content type"));
- auto currentStage = current_stage();
+ auto currentStage = this->shared_from_this();
return next_stage()->propagate(request).then([currentStage](http_response response) -> http_response
{