diff --git a/.gitignore b/.gitignore index 8cba58c6d3..d0ff65b0ac 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,6 @@ Intermediate/ **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ - +.vs/ +# Ignore cmake building directories +build.*/ diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index d99fa5c877..cbe840b663 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -1,12 +1,16 @@ set(CMAKE_LEGACY_CYGWIN_WIN32 0) cmake_minimum_required(VERSION 2.6) -project(casablanca) +project(cpprest) enable_testing() set(WARNINGS) set(ANDROID_STL_FLAGS) +option(WERROR "Threat Warnings as Errors" ON) +option(BUILD_TESTS "Build tests." ON) +option(CPPREST_EXCLUDE_WEBSOCKETS "Exclude websockets functionality." OFF) + # Platform (not compiler) specific settings if(IOS) set(IOS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Build_iOS") @@ -25,7 +29,6 @@ if(IOS) set(BUILD_SHARED_LIBS OFF) set(BUILD_SAMPLES OFF) - option(BUILD_TESTS "Build tests." ON) elseif(ANDROID) set(Boost_COMPILER "-clang") set(Boost_USE_STATIC_LIBS ON) @@ -71,9 +74,8 @@ elseif(ANDROID) option(BUILD_SHARED_LIBS "Build shared Libraries." OFF) set(BUILD_SAMPLES OFF) - option(BUILD_TESTS "Build tests." ON) elseif(UNIX) # This includes OSX - find_package(Boost REQUIRED COMPONENTS random chrono system thread regex filesystem) + find_package(Boost 1.54 REQUIRED COMPONENTS random chrono system thread regex filesystem) find_package(Threads REQUIRED) if(APPLE AND NOT OPENSSL_ROOT_DIR) # Prefer a homebrew version of OpenSSL over the one in /usr/lib @@ -86,7 +88,6 @@ elseif(UNIX) # This includes OSX find_package(OpenSSL 1.0.0 REQUIRED) option(BUILD_SHARED_LIBS "Build shared Libraries." ON) - option(BUILD_TESTS "Build tests." ON) option(BUILD_SAMPLES "Build samples." ON) option(CASA_INSTALL_HEADERS "Install header files." ON) if(CASA_INSTALL_HEADERS) @@ -99,9 +100,7 @@ elseif(UNIX) # This includes OSX endif() elseif(WIN32) option(BUILD_SHARED_LIBS "Build shared Libraries." ON) - option(BUILD_TESTS "Build tests." ON) option(BUILD_SAMPLES "Build samples." ON) - option(CPPREST_EXCLUDE_WEBSOCKETS "Exclude websockets functionality." OFF) option(Boost_USE_STATIC_LIBS ON) add_definitions(-DUNICODE -D_UNICODE) @@ -115,11 +114,31 @@ elseif(WIN32) endif() add_definitions(${Casablanca_DEFINITIONS} -D_WINSOCK_DEPRECATED_NO_WARNINGS -DWIN32) - if (CPPREST_EXCLUDE_WEBSOCKETS) - add_definitions(-DCPPREST_EXCLUDE_WEBSOCKETS=1) - else() - find_package(Boost 1.55 REQUIRED COMPONENTS random system thread filesystem chrono atomic) - find_package(OpenSSL 1.0 REQUIRED) + if (NOT CPPREST_EXCLUDE_WEBSOCKETS) + set(NUGET_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../packages") + set(PACKAGE_PATHS) + list(APPEND PACKAGE_PATHS "${NUGET_PATH}/boost.1.58.0.0/") + list(APPEND PACKAGE_PATHS "${NUGET_PATH}/boost_system-vc140.1.58.0-vs140rc/") + list(APPEND PACKAGE_PATHS "${NUGET_PATH}/boost_date_time-vc140.1.58.0-vs140rc/") + list(APPEND PACKAGE_PATHS "${NUGET_PATH}/boost_regex-vc140.1.58.0-vs140rc/") + list(APPEND PACKAGE_PATHS "${NUGET_PATH}/openssl.v140.windesktop.msvcstl.static.rt-dyn.x64.1.0.2.1/") + list(APPEND PACKAGE_PATHS "${NUGET_PATH}/zlib.v140.windesktop.msvcstl.static.rt-dyn.1.2.8.8/") + + if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + find_library(Boost_SYSTEM_LIBRARY libboost_system-vc140-mt-gd-1_58.lib PATHS ${PACKAGE_PATHS} PATH_SUFFIXES lib/native/address-model-64/lib) + find_library(Boost_DATE_TIME_LIBRARY libboost_date_time-vc140-mt-gd-1_58.lib PATHS ${PACKAGE_PATHS} PATH_SUFFIXES lib/native/address-model-64/lib) + find_library(Boost_REGEX_LIBRARY libboost_regex-vc140-mt-gd-1_58.lib PATHS ${PACKAGE_PATHS} PATH_SUFFIXES lib/native/address-model-64/lib) + set(Boost_LIBRARIES ${Boost_REGEX_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_DATE_TIME_LIBRARY}) + + find_library(OpenSSL_libeay_LIBRARY libeay32.lib PATHS ${PACKAGE_PATHS} PATH_SUFFIXES lib/native/v140/windesktop/msvcstl/static/rt-dyn/x64/debug) + find_library(OpenSSL_ssleay_LIBRARY ssleay32.lib PATHS ${PACKAGE_PATHS} PATH_SUFFIXES lib/native/v140/windesktop/msvcstl/static/rt-dyn/x64/debug) + find_library(ZLIB_LIBRARY zlibstaticd.lib PATHS ${PACKAGE_PATHS} PATH_SUFFIXES lib/native/v140/windesktop/msvcstl/static/rt-dyn/x64/debug) + set(OPENSSL_LIBRARIES ${OpenSSL_ssleay_LIBRARY} ${OpenSSL_libeay_LIBRARY} ${ZLIB_LIBRARY}) + + set(OPENSSL_INCLUDE_DIR "${NUGET_PATH}/openssl.v140.windesktop.msvcstl.static.rt-dyn.x64.1.0.2.1/build/native/include") + endif() + + set(Boost_INCLUDE_DIR "${NUGET_PATH}/boost.1.58.0.0/lib/native/include") endif() else() message(FATAL_ERROR "-- Unsupported Build Platform.") @@ -158,11 +177,20 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") message("-- Setting msvc options") set(WARNINGS) + set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4264") + add_compile_options(/bigobj) + if (WINDOWS_STORE OR WINDOWS_PHONE) + add_compile_options(/ZW) + endif() else() message("-- Unknown compiler, success is doubtful.") message("CMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}") endif() +if (CPPREST_EXCLUDE_WEBSOCKETS) + add_definitions(-DCPPREST_EXCLUDE_WEBSOCKETS=1) +endif() + # Reconfigure final output directory set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) @@ -188,16 +216,8 @@ else() set(Casablanca_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) endif() -set(Casablanca_LIBRARY ${LIB}cpprest) -set(Casablanca_LIBRARIES ${Casablanca_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_SYSTEM_LIBRARY} - ${Boost_THREAD_LIBRARY} - ${Boost_ATOMIC_LIBRARY} - ${Boost_CHRONO_LIBRARY} - ${Boost_RANDOM_LIBRARY} - ${Boost_REGEX_LIBRARY} - ${Boost_FRAMEWORK}) +set(Casablanca_LIBRARY cpprest) +set(Casablanca_LIBRARIES cpprest ${Boost_LIBRARIES}) # Everything in the project needs access to the casablanca include directories include_directories(${Casablanca_INCLUDE_DIRS}) @@ -208,16 +228,16 @@ function(add_casablanca_test NAME SOURCES_VAR) message("-- Added test library ${NAME}") if (NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT") target_link_libraries(${NAME} - ${LIB}httptest_utilities - ${LIB}common_utilities - ${LIB}unittestpp + httptest_utilities + common_utilities + unittestpp ${Casablanca_LIBRARIES} ${ANDROID_STL_FLAGS} ) if (BUILD_SHARED_LIBS) add_test(NAME ${NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - COMMAND test_runner lib${NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} + COMMAND test_runner $ ) endif() endif() diff --git a/Release/include/cpprest/containerstream.h b/Release/include/cpprest/containerstream.h index 89ad1bff6d..12c1af2fe9 100644 --- a/Release/include/cpprest/containerstream.h +++ b/Release/include/cpprest/containerstream.h @@ -439,8 +439,8 @@ namespace Concurrency { namespace streams { size_t newPos = m_current_position + read_size; - auto readBegin = begin(m_data) + m_current_position; - auto readEnd = begin(m_data) + newPos; + auto readBegin = std::begin(m_data) + m_current_position; + auto readEnd = std::begin(m_data) + newPos; #ifdef _WIN32 // Avoid warning C4996: Use checked iterators under SECURE_SCL @@ -470,7 +470,7 @@ namespace Concurrency { namespace streams { resize_for_write(newSize); // Copy the data - std::copy(ptr, ptr + count, begin(m_data) + m_current_position); + std::copy(ptr, ptr + count, std::begin(m_data) + m_current_position); // Update write head and satisfy pending reads if any update_current_position(newSize); 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_constants.dat b/Release/include/cpprest/details/http_constants.dat index a36e931a7d..c408556b19 100644 --- a/Release/include/cpprest/details/http_constants.dat +++ b/Release/include/cpprest/details/http_constants.dat @@ -60,6 +60,7 @@ DAT(accept_charset, "Accept-Charset") DAT(accept_encoding, "Accept-Encoding") DAT(accept_language, "Accept-Language") DAT(accept_ranges, "Accept-Ranges") +DAT(access_control_allow_origin, "Access-Control-Allow-Origin") DAT(age, "Age") DAT(allow, "Allow") DAT(authorization, "Authorization") 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/http_server_asio.h b/Release/include/cpprest/details/http_server_asio.h index 4ab7c38e90..95c1745083 100644 --- a/Release/include/cpprest/details/http_server_asio.h +++ b/Release/include/cpprest/details/http_server_asio.h @@ -72,6 +72,7 @@ class connection bool m_chunked; std::atomic m_refs; // track how many threads are still referring to this + std::unique_ptr m_ssl_context; std::unique_ptr> m_ssl_stream; public: @@ -85,9 +86,10 @@ class connection { if (is_https) { - boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23); - ssl_context_callback(ssl_context); - m_ssl_stream = utility::details::make_unique>(*m_socket, ssl_context); + m_ssl_context = utility::details::make_unique(boost::asio::ssl::context::sslv23); + ssl_context_callback(*m_ssl_context); + m_ssl_stream = utility::details::make_unique>(*m_socket, *m_ssl_context); + m_ssl_stream->async_handshake(boost::asio::ssl::stream_base::server, [this](const boost::system::error_code&) { this->start_request_response(); }); } else 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..8f9e0f9f8a 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 __cdecl(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; + std::shared_ptr<::web::http::client::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/include/pplx/pplxtasks.h b/Release/include/pplx/pplxtasks.h index e82a14e17e..760a614538 100644 --- a/Release/include/pplx/pplxtasks.h +++ b/Release/include/pplx/pplxtasks.h @@ -3093,6 +3093,47 @@ namespace details template static std::false_type _IsValidCreateAsync(_Ty _Param, ...); #endif /* defined (__cplusplus_winrt) */ + +/// +/// A helper class template that makes only movable functions be able to be passed to std::function +/// + template + struct _NonCopyableFunctorWrapper + { + template, + typename std::decay<_Tx>::type>::value>::type> + explicit _NonCopyableFunctorWrapper(_Tx&& f) + : _M_functor{std::make_shared<_Ty>(std::forward<_Tx>(f))} + {} + + template + auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) + { + return _M_functor->operator()(std::forward<_Args>(args)...); + } + + template + auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) + { + return _M_functor->operator()(std::forward<_Args>(args)...); + } + + std::shared_ptr<_Ty> _M_functor; + }; + + template + struct _CopyableFunctor + { + typedef _Ty _Type; + }; + + template + struct _CopyableFunctor<_Ty, typename std::enable_if< + std::is_move_constructible<_Ty>::value && !std::is_copy_constructible<_Ty>::value>::type> + { + typedef _NonCopyableFunctorWrapper<_Ty> _Type; + }; } /// /// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a @@ -3424,11 +3465,11 @@ class task /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + auto then(_Function&& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { task_options _TaskOptions; details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); } /// @@ -3457,10 +3498,10 @@ class task /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + auto then(_Function&& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); } /// @@ -3493,11 +3534,11 @@ class task /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { task_options _TaskOptions(_CancellationToken, _ContinuationContext); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); } /// @@ -3682,13 +3723,13 @@ class task /// This function is Used for runtime internal continuations only. /// template - auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, + auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState, details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { // inherit from antecedent auto _Scheduler = _GetImpl()->_GetScheduler(); - return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); } private: @@ -3801,16 +3842,17 @@ class task typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; - _Function _M_function; + typename details::_CopyableFunctor::type >::_Type _M_function; + template _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, - const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) + _ForwardedFunction&& _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) : details::_PPLTaskHandle::_Type, _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> ::_PPLTaskHandle(_ContinuationImpl) , _M_ancestorTaskImpl(_AncestorImpl) - , _M_function(_Func) + , _M_function(std::forward<_ForwardedFunction>(_Func)) { this->_M_isTaskBasedContinuation = _IsTaskBased::value; this->_M_continuationContext = _Context; @@ -4095,7 +4137,7 @@ class task } template - auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + auto _ThenImpl(_Function&& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType { if (!_M_Impl) { @@ -4105,14 +4147,14 @@ class task details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); - return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); + return _ThenImpl<_InternalReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); } /// /// The one and only implementation of then for void and non-void tasks. /// template - auto _ThenImpl(const _Function& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, + auto _ThenImpl(_Function&& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType { if (!_M_Impl) @@ -4149,7 +4191,7 @@ class task _ContinuationTask._SetTaskCreationCallstack(_CreationStack); _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( - _GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode)); + _GetImpl(), _ContinuationTask._GetImpl(), std::forward<_Function>(_Func), _ContinuationContext, _InliningMode)); return _ContinuationTask; } @@ -4371,10 +4413,10 @@ class task /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + auto then(_Function&& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _M_unitTask._ThenImpl(_Func, _TaskOptions); + return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _TaskOptions); } /// @@ -4407,11 +4449,11 @@ class task /**/ template __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { task_options _TaskOptions(_CancellationToken, _ContinuationContext); details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _M_unitTask._ThenImpl(_Func, _TaskOptions); + return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _TaskOptions); } /// @@ -4555,13 +4597,13 @@ class task /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. /// template - auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState, + auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState, details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { // inherit from antecedent auto _Scheduler = _GetImpl()->_GetScheduler(); - return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); } private: diff --git a/Release/samples/BingRequest/CMakeLists.txt b/Release/samples/BingRequest/CMakeLists.txt index f4de12f102..261078cb76 100644 --- a/Release/samples/BingRequest/CMakeLists.txt +++ b/Release/samples/BingRequest/CMakeLists.txt @@ -1,2 +1,4 @@ -add_executable(BingRequest bingrequest.cpp) -target_link_libraries(BingRequest ${Casablanca_LIBRARIES}) \ No newline at end of file +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + add_executable(BingRequest bingrequest.cpp) + target_link_libraries(BingRequest ${Casablanca_LIBRARIES}) +endif() \ No newline at end of file diff --git a/Release/samples/BlackJack/CMakeLists.txt b/Release/samples/BlackJack/CMakeLists.txt index be0ea12e9e..422ec4c4cc 100644 --- a/Release/samples/BlackJack/CMakeLists.txt +++ b/Release/samples/BlackJack/CMakeLists.txt @@ -1,2 +1,6 @@ -add_subdirectory(BlackJack_Server) -add_subdirectory(BlackJack_Client) +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + add_subdirectory(BlackJack_Server) + add_subdirectory(BlackJack_Client) +else() + # TODO: add BlackJack_UIClient +endif() diff --git a/Release/samples/Oauth1Client/CMakeLists.txt b/Release/samples/Oauth1Client/CMakeLists.txt index a67ab7f547..ee8c82f2dc 100644 --- a/Release/samples/Oauth1Client/CMakeLists.txt +++ b/Release/samples/Oauth1Client/CMakeLists.txt @@ -1,7 +1,8 @@ - -add_executable(oauth1client - Oauth1Client.cpp - stdafx.cpp - ) - -target_link_libraries(oauth1client ${Casablanca_LIBRARIES}) +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + add_executable(oauth1client + Oauth1Client.cpp + stdafx.cpp + ) + + target_link_libraries(oauth1client ${Casablanca_LIBRARIES}) +endif() \ No newline at end of file diff --git a/Release/samples/Oauth2Client/CMakeLists.txt b/Release/samples/Oauth2Client/CMakeLists.txt index 0ed8385cf6..212afb2001 100644 --- a/Release/samples/Oauth2Client/CMakeLists.txt +++ b/Release/samples/Oauth2Client/CMakeLists.txt @@ -1,7 +1,8 @@ +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + add_executable(oauth2client + Oauth2Client.cpp + stdafx.cpp + ) -add_executable(oauth2client - Oauth2Client.cpp - stdafx.cpp - ) - -target_link_libraries(oauth2client ${Casablanca_LIBRARIES}) + target_link_libraries(oauth2client ${Casablanca_LIBRARIES}) +endif() \ No newline at end of file diff --git a/Release/samples/SearchFile/CMakeLists.txt b/Release/samples/SearchFile/CMakeLists.txt index e1313e6405..d933df6781 100644 --- a/Release/samples/SearchFile/CMakeLists.txt +++ b/Release/samples/SearchFile/CMakeLists.txt @@ -1,2 +1,4 @@ -add_executable(SearchFile searchfile.cpp) -target_link_libraries(SearchFile ${Casablanca_LIBRARIES}) +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + add_executable(SearchFile searchfile.cpp) + target_link_libraries(SearchFile ${Casablanca_LIBRARIES}) +endif() diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt index fd1df86e40..cf086ed47e 100644 --- a/Release/src/CMakeLists.txt +++ b/Release/src/CMakeLists.txt @@ -3,8 +3,21 @@ if (NOT CPPREST_EXCLUDE_WEBSOCKETS) include_directories(${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) endif() +add_definitions(${WARNINGS}) + +file(GLOB SOURCES_CPPREST "../include/cpprest/*.h") +file(GLOB SOURCES_PPLX "../include/pplx/*.h") +file(GLOB SOURCES_DETAILS "../include/cpprest/details/*.h") +source_group("Header Files\\cpprest" FILES ${SOURCES_CPPREST}) +source_group("Header Files\\pplx" FILES ${SOURCES_PPLX}) +source_group("Header Files\\cpprest\\details" FILES ${SOURCES_DETAILS}) set(SOURCES_COMMON + ${SOURCES_CPPREST} + ${SOURCES_PPLX} + ${SOURCES_DETAILS} + http/client/http_client.cpp http/client/http_client_msg.cpp + http/client/http_client_impl.h http/client/x509_cert_utilities.cpp http/common/http_helpers.cpp http/common/http_msg.cpp @@ -25,7 +38,6 @@ set(SOURCES_COMMON utilities/web_utilities.cpp websockets/client/ws_msg.cpp websockets/client/ws_client.cpp - websockets/client/ws_client_wspp.cpp ) # THE ORDER OF FILES IS VERY /VERY/ IMPORTANT @@ -37,59 +49,80 @@ if(UNIX) http/client/http_client_asio.cpp http/listener/http_server_asio.cpp ) + if (NOT CPPREST_EXCLUDE_WEBSOCKETS) + list(APPEND SOURCES websockets/client/ws_client_wspp.cpp) + endif() 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() list(APPEND SOURCES pplx/pplxlinux.cpp) endif() - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNINGS} -Werror -pedantic") + + if(WERROR) + add_compile_options(-Werror) + endif() + add_compile_options(-pedantic) 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 + set(SOURCES ${SOURCES_COMMON} pplx/pplxwin.cpp - streams/fileio_win32.cpp - pch/stdafx.cpp ) - set(EXTRALINKS - bcrypt.lib - crypt32.lib - httpapi.lib - Winhttp.lib - ) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNINGS}") - if (${CMAKE_GENERATOR} MATCHES "Visual Studio .*") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Yustdafx.h /Zm200") - set_source_files_properties(pch/stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h") + if (WINDOWS_PHONE OR WINDOWS_STORE) + list(APPEND SOURCES + http/client/http_client_winrt.cpp + streams/fileio_winrt.cpp + websockets/client/ws_client_winrt.cpp + ) + else() + list(APPEND SOURCES + http/client/http_client_winhttp.cpp + http/listener/http_server_httpsys.cpp + streams/fileio_win32.cpp + ) + if (NOT CPPREST_EXCLUDE_WEBSOCKETS) + list(APPEND SOURCES websockets/client/ws_client_wspp.cpp) + endif() + set(EXTRALINKS + bcrypt.lib + crypt32.lib + httpapi.lib + Winhttp.lib + ) + endif() + add_compile_options(/Yustdafx.h /Zm200) + set_source_files_properties(pch/stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h") + + if (NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio .*") + set_property(SOURCE pch/stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/stdafx.pch") + set_property(SOURCE ${SOURCES} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/stdafx.pch") endif() + list(APPEND SOURCES pch/stdafx.cpp pch/stdafx.h) + if (BUILD_SHARED_LIBS) add_definitions(-D_ASYNCRT_EXPORT -D_PPLX_EXPORT -D_USRDLL) endif() endif() -add_library(${Casablanca_LIBRARY} ${SOURCES}) +add_library(cpprest ${SOURCES}) -target_link_libraries(${Casablanca_LIBRARY} +target_link_libraries(cpprest ${CMAKE_THREAD_LIBS_INIT} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ${Boost_ATOMIC_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_RANDOM_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_DATE_TIME_LIBRARY} ${EXTRALINKS} ${Boost_FRAMEWORK} ${OPENSSL_LIBRARIES} @@ -103,17 +136,17 @@ set (CPPREST_VERSION_MINOR 8) set (CPPREST_VERSION_REVISION 0) if(WIN32) - set_target_properties(${Casablanca_LIBRARY} PROPERTIES - OUTPUT_NAME "${Casablanca_LIBRARY}_${CPPREST_VERSION_MAJOR}_${CPPREST_VERSION_MINOR}") + set_target_properties(cpprest PROPERTIES + OUTPUT_NAME "cpprest_${CPPREST_VERSION_MAJOR}_${CPPREST_VERSION_MINOR}") elseif(ANDROID) # Do not use SOVERSION on android. It is completely unsupported (and causes problems). # Perhaps revisit in the future? (NDK r9d, 8/7/14) else() - set_target_properties(${Casablanca_LIBRARY} PROPERTIES + set_target_properties(cpprest PROPERTIES SOVERSION ${CPPREST_VERSION_MAJOR}.${CPPREST_VERSION_MINOR}) install( - TARGETS ${Casablanca_LIBRARY} + TARGETS cpprest LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) 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/vs14.android/casablanca140.android.vcxproj b/Release/src/build/vs14.android/casablanca140.android.vcxproj index 72b51b7e3f..6e7d82feaf 100644 --- a/Release/src/build/vs14.android/casablanca140.android.vcxproj +++ b/Release/src/build/vs14.android/casablanca140.android.vcxproj @@ -60,8 +60,10 @@ + true + true @@ -113,6 +115,12 @@ $(CppRestBaseFileName)140$(DebugFileSuffix)_android_$(CppRestSDKVersionFileSuffix) + + true + + + true + diff --git a/Release/src/build/vs14.static/casablanca140.static.vcxproj b/Release/src/build/vs14.static/casablanca140.static.vcxproj new file mode 100644 index 0000000000..cda12db6a7 --- /dev/null +++ b/Release/src/build/vs14.static/casablanca140.static.vcxproj @@ -0,0 +1,84 @@ + + + + + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A} + Win32Proj + SAK + SAK + SAK + SAK + StaticLibrary + v140 + false + false + cpprestsdk140.static + 8.1 + + + + + + + + + + + + + + + + + + + d + + + lib$(CppRestBaseFileName)140$(DebugFileSuffix)_$(CppRestSDKVersionFileSuffix) + + + + Designer + + + + + _NO_ASYNCRTIMP;_ASYNCRT_EXPORT;_PPLX_EXPORT;WIN32;_MBCS;_USRDLL;%(PreprocessorDefinitions) + $(CasablancaIncludeDir);$(CasablancaSrcDir)\pch;$(WebsocketppIncludeDir);%(AdditionalIncludeDirectories) + Use + stdafx.h + -Zm300 /bigobj %(AdditionalOptions) + MultiThreadedDebugDLL + MultiThreadedDLL + true + + 4503;%(DisableSpecificWarnings) + 4503;%(DisableSpecificWarnings) + 4503;%(DisableSpecificWarnings) + 4503;%(DisableSpecificWarnings) + 4503;%(DisableSpecificWarnings) + 4503;%(DisableSpecificWarnings) + false + false + false + false + false + false + + + Winhttp.lib;httpapi.lib;bcrypt.lib;crypt32.lib;%(AdditionalDependencies) + UseLinkTimeCodeGeneration + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/Release/src/build/vs14.static/packages.config b/Release/src/build/vs14.static/packages.config new file mode 100644 index 0000000000..21a51fdf83 --- /dev/null +++ b/Release/src/build/vs14.static/packages.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ 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..9f71603d85 --- /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 __cdecl(http_request, std::shared_ptr)> &handler) +{ + class function_pipeline_wrapper : public http::http_pipeline_stage + { + public: + function_pipeline_wrapper(const std::function __cdecl(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 a80f2ad1bf..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 std::move(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 std::move(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 std::move(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/json/json_parsing.cpp b/Release/src/json/json_parsing.cpp index ea7ee52e89..13e3d59006 100644 --- a/Release/src/json/json_parsing.cpp +++ b/Release/src/json/json_parsing.cpp @@ -63,7 +63,8 @@ void CreateException(const Token &tk, const utility::string_t &message) { utility::ostringstream_t os; os << _XPLATSTR("* Line ") << tk.start.m_line << _XPLATSTR(", Column ") << tk.start.m_column << _XPLATSTR(" Syntax error: ") << message; - throw web::json::json_exception(os.str().c_str()); + utility::string_t osStr = os.str(); + throw web::json::json_exception(osStr.c_str()); } template diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index e9647bb276..104719c81d 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -226,7 +226,7 @@ static int hex_char_digit_to_decimal_char(int hex) } else { - throw uri_exception("Invalid hexidecimal digit"); + throw uri_exception("Invalid hexadecimal digit"); } return decimal; } @@ -240,12 +240,12 @@ utility::string_t uri::decode(const utility::string_t &encoded) { if(++iter == encoded.end()) { - throw uri_exception("Invalid URI string, two hexidecimal digits must follow '%'"); + throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } int decimal_value = hex_char_digit_to_decimal_char(static_cast(*iter)) << 4; if(++iter == encoded.end()) { - throw uri_exception("Invalid URI string, two hexidecimal digits must follow '%'"); + throw uri_exception("Invalid URI string, two hexadecimal digits must follow '%'"); } decimal_value += hex_char_digit_to_decimal_char(static_cast(*iter)); diff --git a/Release/src/utilities/asyncrt_utils.cpp b/Release/src/utilities/asyncrt_utils.cpp index cb7a329487..d1af12b8dd 100644 --- a/Release/src/utilities/asyncrt_utils.cpp +++ b/Release/src/utilities/asyncrt_utils.cpp @@ -440,7 +440,7 @@ utf16string __cdecl conversions::latin1_to_utf16(const std::string &s) dest.resize(s.size()); for (size_t i = 0; i < s.size(); ++i) { - dest[i] = utf16char(s[i]); + dest[i] = utf16char(static_cast(s[i])); } return dest; } @@ -486,13 +486,13 @@ utility::string_t __cdecl conversions::to_string_t(const std::string &s) #endif } -std::string __cdecl conversions::to_utf8string(std::string value) { return std::move(value); } +std::string __cdecl conversions::to_utf8string(std::string value) { return value; } std::string __cdecl conversions::to_utf8string(const utf16string &value) { return utf16_to_utf8(value); } utf16string __cdecl conversions::to_utf16string(const std::string &value) { return utf8_to_utf16(value); } -utf16string __cdecl conversions::to_utf16string(utf16string value) { return std::move(value); } +utf16string __cdecl conversions::to_utf16string(utf16string value) { return value; } #ifndef WIN32 datetime datetime::timeval_to_datetime(const timeval &time) diff --git a/Release/src/websockets/client/ws_client_wspp.cpp b/Release/src/websockets/client/ws_client_wspp.cpp index e8f5d2edbe..6b5971e481 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__) @@ -61,6 +62,26 @@ #endif /* __GNUC__ */ +// This is a hack to avoid memory leak reports from the debug MSVC CRT for all +// programs using the library: ASIO calls SSL_library_init() which calls +// SSL_COMP_get_compression_methods(), which allocates some heap memory and the +// only way to free it later is to call SSL_COMP_free_compression_methods(), +// but this function is unaccessible from the application code as OpenSSL is +// statically linked into the C++ REST SDK DLL. So, just to be nice, call it +// here ourselves -- even if the real problem is in ASIO (up to v1.60.0). +#if defined(_WIN32) && !defined(NDEBUG) + +#include +static struct ASIO_SSL_memory_leak_suppress +{ + ~ASIO_SSL_memory_leak_suppress() + { + ::SSL_COMP_free_compression_methods(); + } +} ASIO_SSL_memory_leak_suppressor; + +#endif /* _WIN32 && !NDEBUG */ + using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; diff --git a/Release/tests/common/TestRunner/CMakeLists.txt b/Release/tests/common/TestRunner/CMakeLists.txt index 57e63348b9..29669d277a 100644 --- a/Release/tests/common/TestRunner/CMakeLists.txt +++ b/Release/tests/common/TestRunner/CMakeLists.txt @@ -7,7 +7,13 @@ set(TR_SOURCES test_module_loader.cpp ) -add_definitions(-DDESKTOP_TEST_RUNNER) +if (WIN32) + if (WINDOWS_STORE OR WINDOWS_PHONE) + add_definitions(-DWINRT_TEST_RUNNER -D_CONSOLE) + else() + add_definitions(-DDESKTOP_TEST_RUNNER) + endif() +endif() if(NOT IOS AND NOT ANDROID) if(BUILD_SHARED_LIBS) @@ -17,10 +23,8 @@ if(NOT IOS AND NOT ANDROID) ) target_link_libraries(test_runner - ${Boost_FRAMEWORK} - ${Boost_SYSTEM_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${LIB}unittestpp + ${Boost_LIBRARIES} + unittestpp ${CMAKE_DL_LIBS} ) else() @@ -31,31 +35,29 @@ if(NOT IOS AND NOT ANDROID) ) target_link_libraries(test_runner - ${Boost_FRAMEWORK} - ${Boost_SYSTEM_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${LIB}unittestpp + ${Boost_LIBRARIES} + unittestpp ${CMAKE_DL_LIBS} -Wl,-force_load - ${LIB}httpclient_test + httpclient_test -Wl,-force_load - ${LIB}json_test + json_test -Wl,-force_load - ${LIB}uri_test + uri_test -Wl,-force_load - ${LIB}pplx_test + pplx_test -Wl,-force_load - ${LIB}httplistener_test + httplistener_test -Wl,-force_load - ${LIB}streams_test + streams_test -Wl,-force_load - ${LIB}utils_test + utils_test ) elseif(UNIX) add_executable(test_runner @@ -64,39 +66,48 @@ if(NOT IOS AND NOT ANDROID) ) target_link_libraries(test_runner - ${Boost_FRAMEWORK} - ${Boost_SYSTEM_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${LIB}unittestpp + ${Boost_LIBRARIES} + unittestpp ${CMAKE_DL_LIBS} -Wl,--whole-archive - ${LIB}httpclient_test - ${LIB}json_test - ${LIB}uri_test - ${LIB}pplx_test - ${LIB}httplistener_test - ${LIB}streams_test - ${LIB}utils_test + httpclient_test + json_test + uri_test + pplx_test + httplistener_test + streams_test + utils_test -Wl,--no-whole-archive ) else() # In order to achieve --whole-archive on windows, we link all the test files into the test_runner directly # This means that the tests themselves must be created as "OBJECT" libraries - add_executable(test_runner + set(SOURCES test_runner.cpp - test_module_loader.cpp - $ - $ - $ - $ - $ - $ - $ + $ + $ + $ + $ + $ + $ + ) + if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + list(APPEND SOURCES + test_module_loader.cpp + $ ) + endif() + add_executable(test_runner ${SOURCES}) target_link_libraries(test_runner - ${LIB}unittestpp + unittestpp + common_utilities + httptest_utilities + cpprest ${CMAKE_DL_LIBS} ) + if (WINDOWS_STORE) + target_link_libraries(test_runner ucrtd.lib vcruntimed.lib vccorlibd.lib msvcrtd.lib msvcprtd.lib concrtd.lib RuntimeObject.lib) + endif() endif() endif() endif() diff --git a/Release/tests/common/UnitTestpp/CMakeLists.txt b/Release/tests/common/UnitTestpp/CMakeLists.txt index c8e229dd7e..2c8205aa13 100644 --- a/Release/tests/common/UnitTestpp/CMakeLists.txt +++ b/Release/tests/common/UnitTestpp/CMakeLists.txt @@ -38,15 +38,15 @@ set(TEST_SOURCES ) if(UNIX) - set(UT_SOURCES ${UT_SOURCES} + list(APPEND UT_SOURCES src/Posix/SignalTranslator.cpp src/Posix/TimeHelpers.cpp ) elseif(WIN32) - set(UT_SOURCES ${UT_SOURCES} src/Win32/TimeHelpers.cpp) + list(APPEND UT_SOURCES src/Win32/TimeHelpers.cpp) add_definitions(-DWIN32 -D_USRDLL -D_CRT_SECURE_NO_DEPRECATE -DUNITTEST_DLL_EXPORT) endif() -add_library(${LIB}unittestpp ${UT_SOURCES}) -target_link_libraries(${LIB}unittestpp ${ANDROID_STL_FLAGS}) \ No newline at end of file +add_library(unittestpp ${UT_SOURCES}) +target_link_libraries(unittestpp ${ANDROID_STL_FLAGS}) \ No newline at end of file diff --git a/Release/tests/common/utilities/CMakeLists.txt b/Release/tests/common/utilities/CMakeLists.txt index 0b5996c834..6ff564f84e 100644 --- a/Release/tests/common/utilities/CMakeLists.txt +++ b/Release/tests/common/utilities/CMakeLists.txt @@ -4,14 +4,14 @@ if(WIN32) add_definitions(-DCOMMONUTILITIES_EXPORTS) endif() -add_library(${LIB}common_utilities +add_library(common_utilities os_utilities.cpp stdafx.cpp ) -target_link_libraries(${LIB}common_utilities +target_link_libraries(common_utilities ${Casablanca_LIBRARY} - ${LIB}unittestpp + unittestpp ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} ) diff --git a/Release/tests/functional/http/client/CMakeLists.txt b/Release/tests/functional/http/client/CMakeLists.txt index dd9a2b89d0..d18060894f 100644 --- a/Release/tests/functional/http/client/CMakeLists.txt +++ b/Release/tests/functional/http/client/CMakeLists.txt @@ -29,4 +29,4 @@ set(SOURCES http_client_fuzz_tests.cpp ) -add_casablanca_test(${LIB}httpclient_test SOURCES) +add_casablanca_test(httpclient_test SOURCES) diff --git a/Release/tests/functional/http/client/outside_tests.cpp b/Release/tests/functional/http/client/outside_tests.cpp index e0f4498923..dfdacfb2bd 100644 --- a/Release/tests/functional/http/client/outside_tests.cpp +++ b/Release/tests/functional/http/client/outside_tests.cpp @@ -140,7 +140,7 @@ TEST(server_selfsigned_cert) }); } -TEST(server_hostname_mismatch) +TEST(server_hostname_mismatch, "Ignore", "Site fixed certificate. Improve test (new site or alternate method).") { handle_timeout([] { 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 { diff --git a/Release/tests/functional/http/client/proxy_tests.cpp b/Release/tests/functional/http/client/proxy_tests.cpp index d309c957e2..895d7c9518 100644 --- a/Release/tests/functional/http/client/proxy_tests.cpp +++ b/Release/tests/functional/http/client/proxy_tests.cpp @@ -46,7 +46,7 @@ TEST_FIXTURE(uri_address, auto_discovery_proxy) p_request->reply(200); }); http_client_config config; - + config.set_proxy(web_proxy::use_auto_discovery); VERIFY_IS_FALSE(config.proxy().is_disabled()); VERIFY_IS_FALSE(config.proxy().is_specified()); @@ -63,7 +63,7 @@ TEST_FIXTURE(uri_address, disabled_proxy) http_asserts::assert_test_request_equals(p_request, methods::PUT, U("/"), U("text/plain"), U("sample data")); p_request->reply(status_codes::OK); }); - + http_client_config config; config.set_proxy(web_proxy(web_proxy::disabled)); VERIFY_IS_TRUE(config.proxy().is_disabled()); @@ -97,13 +97,13 @@ TEST_FIXTURE(uri_address, no_proxy_options_on_winrt) web_proxy proxy(u); VERIFY_IS_TRUE(proxy.is_specified()); VERIFY_ARE_EQUAL(u, proxy.address()); - credentials cred(U("artur"), U("fred")); // relax, this is not my real password + web::credentials cred(U("artur"), U("fred")); // relax, this is not my real password proxy.set_credentials(cred); http_client_config config; config.set_proxy(proxy); - // Access to this server will succeed because the first request will not be challenged and hence + // Access to this server will succeed because the first request will not be challenged and hence // my bogus credentials will not be supplied. http_client client(U("http://www.microsoft.com"), config); diff --git a/Release/tests/functional/http/listener/CMakeLists.txt b/Release/tests/functional/http/listener/CMakeLists.txt index 209e4026d8..270b89de25 100644 --- a/Release/tests/functional/http/listener/CMakeLists.txt +++ b/Release/tests/functional/http/listener/CMakeLists.txt @@ -1,20 +1,22 @@ -include_directories (../utilities/include) - -set (SOURCES - building_response_tests.cpp - connections_and_errors.cpp - header_tests.cpp - listener_construction_tests.cpp - reply_helper_tests.cpp - request_extract_tests.cpp - request_handler_tests.cpp - request_relative_uri_tests.cpp - request_stream_tests.cpp - requests_tests.cpp - response_stream_tests.cpp - status_code_reason_phrase_tests.cpp - to_string_tests.cpp - stdafx.cpp -) - -add_casablanca_test(${LIB}httplistener_test SOURCES) +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + include_directories (../utilities/include) + + set (SOURCES + building_response_tests.cpp + connections_and_errors.cpp + header_tests.cpp + listener_construction_tests.cpp + reply_helper_tests.cpp + request_extract_tests.cpp + request_handler_tests.cpp + request_relative_uri_tests.cpp + request_stream_tests.cpp + requests_tests.cpp + response_stream_tests.cpp + status_code_reason_phrase_tests.cpp + to_string_tests.cpp + stdafx.cpp + ) + + add_casablanca_test(httplistener_test SOURCES) +endif () \ No newline at end of file diff --git a/Release/tests/functional/http/utilities/CMakeLists.txt b/Release/tests/functional/http/utilities/CMakeLists.txt index 08f6bcf351..a36e9614e3 100644 --- a/Release/tests/functional/http/utilities/CMakeLists.txt +++ b/Release/tests/functional/http/utilities/CMakeLists.txt @@ -11,10 +11,10 @@ set(SOURCES test_server_utilities.cpp ) -add_library(${LIB}httptest_utilities ${SOURCES}) -target_link_libraries(${LIB}httptest_utilities - ${LIB}unittestpp - ${LIB}common_utilities +add_library(httptest_utilities ${SOURCES}) +target_link_libraries(httptest_utilities + unittestpp + common_utilities ${BOOST_LIBRARIES} ${Casablanca_LIBRARIES} ) diff --git a/Release/tests/functional/json/CMakeLists.txt b/Release/tests/functional/json/CMakeLists.txt index c83741c940..f6d6ff7587 100644 --- a/Release/tests/functional/json/CMakeLists.txt +++ b/Release/tests/functional/json/CMakeLists.txt @@ -3,9 +3,11 @@ set (SOURCES negative_parsing_tests.cpp parsing_tests.cpp to_as_and_operators_tests.cpp - fuzz_tests.cpp iterator_tests.cpp json_numbers_tests.cpp ) +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + list(APPEND SOURCES fuzz_tests.cpp) +endif() -add_casablanca_test(${LIB}json_test SOURCES) +add_casablanca_test(json_test SOURCES) diff --git a/Release/tests/functional/pplx/pplx_test/CMakeLists.txt b/Release/tests/functional/pplx/pplx_test/CMakeLists.txt index 000442f7ab..01c56f7522 100644 --- a/Release/tests/functional/pplx/pplx_test/CMakeLists.txt +++ b/Release/tests/functional/pplx/pplx_test/CMakeLists.txt @@ -5,4 +5,4 @@ set(SOURCES stdafx.cpp ) -add_casablanca_test(${LIB}pplx_test SOURCES) +add_casablanca_test(pplx_test SOURCES) diff --git a/Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp b/Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp index e0d3596e18..e38398f854 100644 --- a/Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp +++ b/Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp @@ -304,6 +304,33 @@ TEST(TestTasks_void_tasks_default_construction) } } +TEST(TestTasks_movable_then) +{ +#ifndef _MSC_VER + // create movable only type + struct A + { + A() = default; + A(A&&) = default; + A& operator=(A&&) = default; + + // explicitly delete copy functions + A(const A&) = delete; + A& operator=(const A&) = delete; + + char operator()(int) + { + return 'c'; + } + } a; + + task task = create_task([]{ return 2; }); + auto f = task.then(std::move(a)); + + IsTrue(f.get() == 'c', L".then should be able to work with movable functors"); +#endif // _MSC_VER +} + TEST(TestTasks_constant_this) { #ifdef _MSC_VER diff --git a/Release/tests/functional/streams/CMakeLists.txt b/Release/tests/functional/streams/CMakeLists.txt index 72ce8177dd..c7e1147966 100644 --- a/Release/tests/functional/streams/CMakeLists.txt +++ b/Release/tests/functional/streams/CMakeLists.txt @@ -4,6 +4,13 @@ set(SOURCES memstream_tests.cpp ostream_tests.cpp stdstream_tests.cpp - fuzz_tests.cpp ) -add_casablanca_test(${LIB}streams_test SOURCES) +if (NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) + list(APPEND SOURCES fuzz_tests.cpp) + if (WIN32) + list(APPEND SOURCES CppSparseFile.cpp) + endif () +else () + list(APPEND SOURCES winrt_interop_tests.cpp) +endif () +add_casablanca_test(streams_test SOURCES) diff --git a/Release/tests/functional/utils/strings.cpp b/Release/tests/functional/utils/strings.cpp index d6a88c70a9..3142ba45db 100644 --- a/Release/tests/functional/utils/strings.cpp +++ b/Release/tests/functional/utils/strings.cpp @@ -278,13 +278,22 @@ TEST(utf8_to_utf16_errors) TEST(latin1_to_utf16) { - // TODO: find some string that actually uses something unique to the Latin1 code page. - std::string str_latin1("This is a test"); - utf16string str_utf16 = utility::conversions::latin1_to_utf16(str_latin1); - - for (size_t i = 0; i < str_latin1.size(); ++i) + char in[256] = { 0 }; + char16_t expectedResult[256] = { 0 }; + for (size_t i = 0; i < 256; ++i) + { + in[i] = static_cast(i); + expectedResult[i] = static_cast(i); + } + + std::string str_latin1(in, 256); + + auto actualResult = utility::conversions::latin1_to_utf16(str_latin1); + + VERIFY_ARE_EQUAL(str_latin1.size(), actualResult.size()); + for (size_t i = 0; i < actualResult.size(); ++i) { - VERIFY_ARE_EQUAL((utf16char)str_latin1[i], str_utf16[i]); + VERIFY_ARE_EQUAL(expectedResult[i], actualResult[i]); } } diff --git a/Release/tests/functional/websockets/CMakeLists.txt b/Release/tests/functional/websockets/CMakeLists.txt index 415f5510fb..cd373a50d5 100644 --- a/Release/tests/functional/websockets/CMakeLists.txt +++ b/Release/tests/functional/websockets/CMakeLists.txt @@ -1,2 +1,21 @@ -add_subdirectory(client) -add_subdirectory(utilities) +if (NOT CPPREST_EXCLUDE_WEBSOCKETS) + + include_directories(utilities/include) + + set(SOURCES + utilities/test_websocket_server.cpp + ) + + add_library(websockettest_utilities ${TEST_LIBRARY_TARGET_TYPE} ${SOURCES}) + target_compile_definitions(websockettest_utilities PRIVATE -DWEBSOCKETTESTUTILITY_EXPORTS) + if (NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT") + target_link_libraries(websockettest_utilities + unittestpp + common_utilities + ${BOOST_LIBRARIES} + ${Casablanca_LIBRARIES} + ) + endif() + + add_subdirectory(client) +endif() diff --git a/Release/tests/functional/websockets/client/CMakeLists.txt b/Release/tests/functional/websockets/client/CMakeLists.txt index e4bd7a3625..9cb347ebb5 100644 --- a/Release/tests/functional/websockets/client/CMakeLists.txt +++ b/Release/tests/functional/websockets/client/CMakeLists.txt @@ -10,6 +10,8 @@ if (NOT CPPREST_EXCLUDE_WEBSOCKETS) send_msg_tests.cpp ) - add_casablanca_test(${LIB}websocketclient_test SOURCES) - target_link_libraries(${LIB}websocketclient_test ${LIB}websockettest_utilities) + add_casablanca_test(websocketclient_test SOURCES) + if (NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT") + target_link_libraries(websocketclient_test websockettest_utilities) + endif() endif() \ No newline at end of file diff --git a/Release/tests/functional/websockets/client/authentication_tests.cpp b/Release/tests/functional/websockets/client/authentication_tests.cpp index 363835d7ad..148655fd66 100644 --- a/Release/tests/functional/websockets/client/authentication_tests.cpp +++ b/Release/tests/functional/websockets/client/authentication_tests.cpp @@ -234,7 +234,7 @@ TEST(self_signed_cert) handshake_error_test_impl(U("wss://www.pcwebshop.co.uk/")); } -TEST(hostname_mismatch) +TEST(hostname_mismatch, "Ignore", "Site fixed certificate. Improve test (new site or alternate method).") { handshake_error_test_impl(U("wss://swordsoftruth.com/")); } diff --git a/Release/tests/functional/websockets/utilities/CMakeLists.txt b/Release/tests/functional/websockets/utilities/CMakeLists.txt deleted file mode 100644 index e1c5b1edba..0000000000 --- a/Release/tests/functional/websockets/utilities/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -if (NOT CPPREST_EXCLUDE_WEBSOCKETS) - - include_directories(include) - - if(WIN32) - add_definitions(-DWEBSOCKETTESTUTILITY_EXPORTS) - endif() - - set(SOURCES - test_websocket_server.cpp - ) - - add_library(${LIB}websockettest_utilities ${SOURCES}) - target_link_libraries(${LIB}websockettest_utilities - ${LIB}unittestpp - ${LIB}common_utilities - ${BOOST_LIBRARIES} - ${Casablanca_LIBRARIES} - ) - -endif() \ No newline at end of file diff --git a/cpprestsdk140.sln b/cpprestsdk140.sln index 36c7cb8da0..2392954c0a 100644 --- a/cpprestsdk140.sln +++ b/cpprestsdk140.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23207.2 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpprestsdk140", "Release\src\build\vs14\casablanca140.vcxproj", "{1014C621-BC2D-4813-B8C1-6D83AD6F9249}" EndProject @@ -43,18 +43,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SearchFile140", "Release\sa EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpprestsdk140.android", "Release\src\build\vs14.android\casablanca140.android.vcxproj", "{AFB49019-965B-4C10-BAFF-C86C16D58010}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpprestsdk140.wod", "Release\src\build\vs14.wod\casablanca140.wod.vcxproj", "{3A584D9C-1A98-4046-B7D3-B7171EF42D34}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpprestsdk140.uwp", "Release\src\build\vs14.uwp\cpprestsdk140.uwp.vcxproj", "{36D79E79-7E9E-4B3A-88A3-9F9B295C80B9}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpprestsdk140.uwp.staticlib", "Release\src\build\vs14.uwp\cpprestsdk140.uwp.staticlib.vcxproj", "{47A5CFDC-C244-45A6-9830-38CB303CB495}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestRunner.android.NativeActivity", "Release\tests\common\TestRunner\vs14.android\TestRunner.android.NativeActivity\TestRunner.android.NativeActivity.vcxproj", "{D1060D0A-A10E-444D-9F6B-9676EA453F9A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpprestsdk140.static", "Release\src\build\vs14.static\casablanca140.static.vcxproj", "{79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution - Release\src\build\win32.vcxitems*{3a584d9c-1a98-4046-b7d3-b7171ef42d34}*SharedItemsImports = 4 - Release\src\build\common.vcxitems*{3a584d9c-1a98-4046-b7d3-b7171ef42d34}*SharedItemsImports = 4 Release\src\build\winrt.vcxitems*{0a9ba181-7876-4b3d-a5e0-ee673fa51c05}*SharedItemsImports = 9 Release\src\build\android.vcxitems*{65951c40-a332-4b54-89c2-7cdaf30d5f66}*SharedItemsImports = 9 Release\src\build\winrt.vcxitems*{47a5cfdc-c244-45a6-9830-38cb303cb495}*SharedItemsImports = 4 @@ -65,6 +61,8 @@ Global Release\src\build\common.vcxitems*{36d79e79-7e9e-4b3a-88a3-9f9b295c80b9}*SharedItemsImports = 4 Release\src\build\android.vcxitems*{afb49019-965b-4c10-baff-c86c16d58010}*SharedItemsImports = 4 Release\src\build\common.vcxitems*{afb49019-965b-4c10-baff-c86c16d58010}*SharedItemsImports = 4 + Release\src\build\win32.vcxitems*{79c9bbec-d7c9-4ba3-b2b3-5c3a14a9f24a}*SharedItemsImports = 4 + Release\src\build\common.vcxitems*{79c9bbec-d7c9-4ba3-b2b3-5c3a14a9f24a}*SharedItemsImports = 4 Release\src\build\win32.vcxitems*{f40f4804-50f9-4257-8d74-b9cbb19ac4c3}*SharedItemsImports = 9 Release\src\build\other.vcxitems*{3d5908f7-7673-4229-bc46-2007a7af9cae}*SharedItemsImports = 9 Release\src\build\common.vcxitems*{594dcb5f-07e3-4084-a2ce-268611fa629f}*SharedItemsImports = 9 @@ -178,22 +176,6 @@ Global {AFB49019-965B-4C10-BAFF-C86C16D58010}.Release|x64.ActiveCfg = Release|x86 {AFB49019-965B-4C10-BAFF-C86C16D58010}.Release|x86.ActiveCfg = Release|x86 {AFB49019-965B-4C10-BAFF-C86C16D58010}.Release|x86.Build.0 = Release|x86 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|ARM.ActiveCfg = Debug|ARM - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|ARM.Build.0 = Debug|ARM - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|Win32.ActiveCfg = Debug|Win32 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|Win32.Build.0 = Debug|Win32 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|x64.ActiveCfg = Debug|x64 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|x64.Build.0 = Debug|x64 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|x86.ActiveCfg = Debug|Win32 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Debug|x86.Build.0 = Debug|Win32 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|ARM.ActiveCfg = Release|ARM - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|ARM.Build.0 = Release|ARM - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|Win32.ActiveCfg = Release|Win32 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|Win32.Build.0 = Release|Win32 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|x64.ActiveCfg = Release|x64 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|x64.Build.0 = Release|x64 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|x86.ActiveCfg = Release|Win32 - {3A584D9C-1A98-4046-B7D3-B7171EF42D34}.Release|x86.Build.0 = Release|Win32 {36D79E79-7E9E-4B3A-88A3-9F9B295C80B9}.Debug|ARM.ActiveCfg = Debug|ARM {36D79E79-7E9E-4B3A-88A3-9F9B295C80B9}.Debug|ARM.Build.0 = Debug|ARM {36D79E79-7E9E-4B3A-88A3-9F9B295C80B9}.Debug|Win32.ActiveCfg = Debug|Win32 @@ -226,20 +208,22 @@ Global {47A5CFDC-C244-45A6-9830-38CB303CB495}.Release|x64.Build.0 = Release|x64 {47A5CFDC-C244-45A6-9830-38CB303CB495}.Release|x86.ActiveCfg = Release|Win32 {47A5CFDC-C244-45A6-9830-38CB303CB495}.Release|x86.Build.0 = Release|Win32 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Debug|ARM.ActiveCfg = Debug|ARM - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Debug|ARM.Build.0 = Debug|ARM - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Debug|Win32.ActiveCfg = Debug|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Debug|Win32.Build.0 = Debug|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Debug|x64.ActiveCfg = Debug|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Debug|x86.ActiveCfg = Debug|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Debug|x86.Build.0 = Debug|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Release|ARM.ActiveCfg = Release|ARM - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Release|ARM.Build.0 = Release|ARM - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Release|Win32.ActiveCfg = Release|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Release|Win32.Build.0 = Release|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Release|x64.ActiveCfg = Release|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Release|x86.ActiveCfg = Release|x86 - {D1060D0A-A10E-444D-9F6B-9676EA453F9A}.Release|x86.Build.0 = Release|x86 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|ARM.ActiveCfg = Debug|ARM + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|ARM.Build.0 = Debug|ARM + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|Win32.ActiveCfg = Debug|Win32 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|Win32.Build.0 = Debug|Win32 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|x64.ActiveCfg = Debug|x64 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|x64.Build.0 = Debug|x64 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|x86.ActiveCfg = Debug|Win32 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Debug|x86.Build.0 = Debug|Win32 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|ARM.ActiveCfg = Release|ARM + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|ARM.Build.0 = Release|ARM + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|Win32.ActiveCfg = Release|Win32 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|Win32.Build.0 = Release|Win32 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|x64.ActiveCfg = Release|x64 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|x64.Build.0 = Release|x64 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|x86.ActiveCfg = Release|Win32 + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -263,8 +247,8 @@ Global {FFBFD6C1-B525-4D35-AB64-A2FE9460B147} = {76FA5645-FF99-44C0-87DB-B96AFAC2F800} {F03BEE03-BEFB-4B17-A774-D9C8246530D4} = {22309B46-EE6F-45D0-A993-2F45D98DEF22} {AFB49019-965B-4C10-BAFF-C86C16D58010} = {64F2F240-04BE-43B2-97BE-DA47FDFE8393} - {3A584D9C-1A98-4046-B7D3-B7171EF42D34} = {64F2F240-04BE-43B2-97BE-DA47FDFE8393} {36D79E79-7E9E-4B3A-88A3-9F9B295C80B9} = {64F2F240-04BE-43B2-97BE-DA47FDFE8393} {47A5CFDC-C244-45A6-9830-38CB303CB495} = {64F2F240-04BE-43B2-97BE-DA47FDFE8393} + {79C9BBEC-D7C9-4BA3-B2B3-5C3A14A9F24A} = {64F2F240-04BE-43B2-97BE-DA47FDFE8393} EndGlobalSection EndGlobal