diff --git a/.gitignore b/.gitignore index 8fc8285e16..3a3fea4411 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,5 @@ Build_android/build/ Generated Files/ # Ignore iOS temp build directories Build_iOS/Apple-Boost-BuildScript + +/out/ diff --git a/.gitmodules b/.gitmodules index 5a33829602..f9d0b58a34 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "vcpkg"] path = vcpkg url = https://github.com/Microsoft/vcpkg -[submodule "websocketspp"] +[submodule "websocketpp"] path = Release/libs/websocketpp - url = https://github.com/zaphoyd/websocketpp/ + url = https://github.com/zaphoyd/websocketpp + fetchRecurseSubmodules = false diff --git a/.vscode/settings.json b/.vscode/settings.json index 2fc5e0c5fe..9bad9cb7c7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,18 @@ "**/.git/objects/**": true, "**/.git/subtree-cache/**": true, "**/node_modules/*/**": true, - "**/vcpkg/**": true - } + "**/vcpkg/**": true, + "build.x86.debug": true, + "build.x86.release": true, + "build.x64.debug": true, + "build.x64.release": true, + "out": true, + }, + "cSpell.words": [ + "XPLATSTR", + "blittable", + "pplx", + "rdpos", + "rgpsz" + ] } diff --git a/Build_android/configure.sh b/Build_android/configure.sh index 0d218a46b4..59b35659ae 100755 --- a/Build_android/configure.sh +++ b/Build_android/configure.sh @@ -25,7 +25,7 @@ DO_OPENSSL=1 DO_CMAKE=1 DO_CPPRESTSDK=1 -BOOSTVER=1.69.0 +BOOSTVER=1.70.0 OPENSSLVER=1.1.0j CMAKEVER=3.14.0 @@ -54,8 +54,8 @@ do DO_OPENSSL=0 ;; "--skip-cmake") - DO_CMAKE=0 - ;; + DO_CMAKE=0 + ;; "--skip-cpprestsdk") DO_CPPRESTSDK=0 ;; @@ -92,6 +92,8 @@ done # Variables setup +unset BOOST_ROOT + if [ ! -e "${ANDROID_NDK}/ndk-build" ] then echo "ANDROID_NDK does not point to a valid NDK." @@ -102,10 +104,10 @@ NDK_DIR=`cd "${ANDROID_NDK}" && pwd` SRC_DIR=`pwd` if [ -z "$NCPU" ]; then - NCPU=4 - if uname -s | grep -i "linux" > /dev/null ; then - NCPU=`cat /proc/cpuinfo | grep -c -i processor` - fi + NCPU=4 + if uname -s | grep -i "linux" > /dev/null ; then + NCPU=`cat /proc/cpuinfo | grep -c -i processor` + fi fi # ----------------------- @@ -149,11 +151,12 @@ if [ "${DO_OPENSSL}" == "1" ]; then ( # ----- # Uses the build script from Moritz Wundke (formerly MysticTreeGames) # https://github.com/moritz-wundke/Boost-for-Android +# (plus the patch https://github.com/o01eg/Boost-for-Android/tree/ndk-bump-21) if [ "${DO_BOOST}" == "1" ]; then ( - if [ ! -d 'Boost-for-Android' ]; then git clone https://github.com/moritz-wundke/Boost-for-Android; fi + if [ ! -d 'Boost-for-Android' ]; then git clone https://github.com/o01eg/Boost-for-Android/; fi cd Boost-for-Android - git checkout 1356b87fed389b4abf1ff671adec0b899877174b + git checkout 7626dd6f7cab7866dce20e685d4a1b11194366a7 PATH="$PATH:$NDK_DIR" \ CXXFLAGS="-std=gnu++11" \ ./build-android.sh \ @@ -184,16 +187,17 @@ if [ "${DO_CMAKE}" == "1" ]; then ( if [ "${DO_CPPRESTSDK}" == "1" ]; then # Use the builtin CMake toolchain configuration that comes with the NDK function build_cpprestsdk { ( - rm -rf $1 + rm -rf $1 ./cmake-${CMAKEVER}/bin/cmake \ -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK}/build/cmake/android.toolchain.cmake" \ -DANDROID_NDK="${ANDROID_NDK}" \ -DANDROID_TOOLCHAIN=clang \ -DANDROID_ABI=$2 \ -DBOOST_VERSION="${BOOSTVER}" \ + -DCPPREST_EXCLUDE_WEBSOCKETS=ON \ -DCMAKE_BUILD_TYPE=$3 \ - -S "${DIR}/.." \ - -B $1 + -S "${DIR}/.." \ + -B $1 make -j $NCPU -C $1 ) } diff --git a/Build_iOS/CMakeLists.txt b/Build_iOS/CMakeLists.txt index 0cf1b62467..f5c5bae75a 100644 --- a/Build_iOS/CMakeLists.txt +++ b/Build_iOS/CMakeLists.txt @@ -1,5 +1,5 @@ project(casablanca-ios NONE) -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.9) enable_testing() diff --git a/Build_iOS/configure.sh b/Build_iOS/configure.sh index d4240d8943..7045be58fc 100755 --- a/Build_iOS/configure.sh +++ b/Build_iOS/configure.sh @@ -73,8 +73,8 @@ while (( "$#" )); do done ## Configuration -DEFAULT_BOOST_VERSION=1.67.0 -DEFAULT_OPENSSL_VERSION=1.0.2o +DEFAULT_BOOST_VERSION=1.69.0 +DEFAULT_OPENSSL_VERSION=1.1.0k BOOST_VERSION=${BOOST_VERSION:-${DEFAULT_BOOST_VERSION}} OPENSSL_VERSION=${OPENSSL_VERSION:-${DEFAULT_OPENSSL_VERSION}} CPPRESTSDK_BUILD_TYPE=${CPPRESTSDK_BUILD_TYPE:-Release} @@ -96,7 +96,7 @@ if [ ! -e $ABS_PATH/boost.framework ] && [ ! -d $ABS_PATH/boost ]; then git clone https://github.com/faithfracture/Apple-Boost-BuildScript ${ABS_PATH}/Apple-Boost-BuildScript fi pushd ${ABS_PATH}/Apple-Boost-BuildScript - git checkout 1b94ec2e2b5af1ee036d9559b96e70c113846392 + git checkout 8c42427b4ebc7865eb99b0a0b9607888af2c6abc BOOST_LIBS="thread chrono filesystem regex system random" ./boost.sh -ios -tvos --boost-version $BOOST_VERSION popd mv ${ABS_PATH}/Apple-Boost-BuildScript/build/boost/${BOOST_VERSION}/ios/framework/boost.framework ${ABS_PATH} @@ -112,7 +112,7 @@ if [ ! -e ${ABS_PATH}/openssl/lib/libcrypto.a ]; then git clone --depth=1 https://github.com/x2on/OpenSSL-for-iPhone.git ${ABS_PATH}/OpenSSL-for-iPhone fi pushd ${ABS_PATH}/OpenSSL-for-iPhone - git checkout 10019638e80e8a8a5fc19642a840d8a69fac7349 + git checkout 6c665e2a15ba7e834875eecaf4eb93c11605dd9a ./build-libssl.sh --version=${OPENSSL_VERSION} popd mkdir -p ${ABS_PATH}/openssl/lib diff --git a/CMakeLists.txt b/CMakeLists.txt index d010e21fcd..4e0377ef27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.9) project(cpprestsdk-root NONE) enable_testing() add_subdirectory(Release) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 845ecd85c0..36348fa544 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -54,3 +54,6 @@ leetal Benjamin Lee (mobileben) René Meusel (reneme) + +Sony Corporation +Gareth Sylvester-Bradley (garethsb-sony) diff --git a/README.md b/README.md index 391027675b..c2e8f3cbdb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**cpprestsdk is in maintenance mode and we do not recommend its use in new projects. We will continue to fix critical bugs and address security issues.** + ## Welcome! The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services. @@ -39,7 +41,7 @@ Once you have the library, look at our [tutorial](https://github.com/Microsoft/c To use from CMake: ```cmake -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.9) project(main) find_package(cpprestsdk REQUIRED) @@ -59,7 +61,7 @@ target_link_libraries(main PRIVATE cpprestsdk::cpprest) Is there a feature missing that you'd like to see, or found a bug that you have a fix for? Or do you have an idea or just interest in helping out in building the library? Let us know and we'd love to work with you. For a good starting point on where we are headed and feature ideas, take a look at our [requested features and bugs](https://github.com/Microsoft/cpprestsdk/issues). -Big or small we'd like to take your [contributions](https://github.com/Microsoft/cpprestsdk/wiki/Make-a-contribution-and-report-issues) back to help improve the C++ Rest SDK for everyone. If interested contact us askcasablanca at Microsoft dot com. +Big or small we'd like to take your [contributions](https://github.com/Microsoft/cpprestsdk/wiki/Make-a-contribution-and-report-issues) back to help improve the C++ Rest SDK for everyone. ## Having Trouble? @@ -70,6 +72,6 @@ We'd love to get your review score, whether good or bad, but even more than that * [FAQ](https://github.com/Microsoft/cpprestsdk/wiki/FAQ) * [Documentation](https://github.com/Microsoft/cpprestsdk/wiki) * [Issue Tracker](https://github.com/Microsoft/cpprestsdk/issues) -* Directly contact us: This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt index 994111f6d6..14e43cedcd 100644 --- a/Release/CMakeLists.txt +++ b/Release/CMakeLists.txt @@ -1,5 +1,5 @@ set(CMAKE_LEGACY_CYGWIN_WIN32 0) -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.9) if(POLICY CMP0042) cmake_policy(SET CMP0042 NEW) # use MACOSX_RPATH endif() @@ -11,7 +11,7 @@ endif() set(CPPREST_VERSION_MAJOR 2) set(CPPREST_VERSION_MINOR 10) -set(CPPREST_VERSION_REVISION 13) +set(CPPREST_VERSION_REVISION 19) enable_testing() @@ -19,7 +19,7 @@ set(WERROR ON CACHE BOOL "Treat Warnings as Errors.") set(CPPREST_EXCLUDE_WEBSOCKETS OFF CACHE BOOL "Exclude websockets functionality.") set(CPPREST_EXCLUDE_COMPRESSION OFF CACHE BOOL "Exclude compression functionality.") set(CPPREST_EXCLUDE_BROTLI ON CACHE BOOL "Exclude Brotli compression functionality.") -set(CPPREST_EXPORT_DIR cpprestsdk CACHE STRING "Directory to install CMake config files.") +set(CPPREST_EXPORT_DIR cmake/cpprestsdk CACHE STRING "Directory to install CMake config files.") set(CPPREST_INSTALL_HEADERS ON CACHE BOOL "Install header files.") set(CPPREST_INSTALL ON CACHE BOOL "Add install commands.") @@ -61,6 +61,7 @@ endif() include(cmake/cpprest_find_boost.cmake) include(cmake/cpprest_find_zlib.cmake) +include(cmake/cpprest_find_winhttppal.cmake) include(cmake/cpprest_find_openssl.cmake) include(cmake/cpprest_find_websocketpp.cmake) include(cmake/cpprest_find_brotli.cmake) @@ -182,8 +183,15 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MP") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MP") + set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} /profile /OPT:REF /OPT:ICF") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /profile /OPT:REF /OPT:ICF") + if (WINDOWS_STORE OR WINDOWS_PHONE) add_compile_options(/ZW) + else() + if (NOT (MSVC_VERSION LESS 1920)) + add_compile_options(/permissive-) + endif() endif() else() message("-- Unknown compiler, success is doubtful.") @@ -195,6 +203,28 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) +function(configure_pch target precompile_header precomile_source) # optional additional compile arguments + if(MSVC) + get_target_property(_srcs ${target} SOURCES) + + set(pch_output_filepath_arg) + if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") + set_property(SOURCE ${precomile_source} APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/${target}.pch") + set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${target}.pch") + set(pch_output_filepath_arg "/Fp${CMAKE_CURRENT_BINARY_DIR}/${target}.pch") + else() + # Don't specify output file so that VS may choose a config spefic location. + # Otherwise Debug/Release builds will interfere with one another. + endif() + + set_source_files_properties(${precomile_source} PROPERTIES COMPILE_FLAGS "/Yc${precompile_header}") + target_sources(${target} PRIVATE ${precomile_source}) + # Note: as ${precomile_source} is also a SOURCE for ${target}, the below options will also be applied. + # ${precomile_source} has /Yc option that will cause the shared /Yu to be ignored. + target_compile_options(${target} PRIVATE /Yu${precompile_header} ${pch_output_filepath_arg} ${ARGN}) + endif() +endfunction() + # These settings can be used by the test targets set(Casablanca_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) set(Casablanca_LIBRARY cpprest) diff --git a/Release/cmake/cpprest_find_openssl.cmake b/Release/cmake/cpprest_find_openssl.cmake index 0b49a7e55c..9333663607 100644 --- a/Release/cmake/cpprest_find_openssl.cmake +++ b/Release/cmake/cpprest_find_openssl.cmake @@ -34,14 +34,24 @@ function(cpprest_find_openssl) if(APPLE) if(NOT DEFINED OPENSSL_ROOT_DIR) # Prefer a homebrew version of OpenSSL over the one in /usr/lib - file(GLOB OPENSSL_ROOT_DIR /usr/local/Cellar/openssl/*) + file(GLOB OPENSSL_ROOT_DIR /usr/local/Cellar/openssl*/*) # Prefer the latest (make the latest one first) list(REVERSE OPENSSL_ROOT_DIR) + list(GET OPENSSL_ROOT_DIR 0 OPENSSL_ROOT_DIR) endif() # This should prevent linking against the system provided 0.9.8y + message(STATUS "OPENSSL_ROOT_DIR = ${OPENSSL_ROOT_DIR}") set(_OPENSSL_VERSION "") endif() - find_package(OpenSSL 1.0.0 REQUIRED) + if(UNIX) + find_package(PkgConfig) + pkg_search_module(OPENSSL openssl) + endif() + if(OPENSSL_FOUND) + target_link_libraries(cpprest PRIVATE ${OPENSSL_LDFLAGS}) + else() + find_package(OpenSSL 1.0.0 REQUIRED) + endif() INCLUDE(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") @@ -67,4 +77,4 @@ function(cpprest_find_openssl) # libressl doesn't ship with the cleanup method being used in ws_client_wspp target_compile_definitions(cpprestsdk_openssl_internal INTERFACE -DCPPREST_NO_SSL_LEAK_SUPPRESS) endif() -endfunction() \ No newline at end of file +endfunction() diff --git a/Release/cmake/cpprest_find_winhttppal.cmake b/Release/cmake/cpprest_find_winhttppal.cmake new file mode 100644 index 0000000000..9a6840fba2 --- /dev/null +++ b/Release/cmake/cpprest_find_winhttppal.cmake @@ -0,0 +1,17 @@ +function(cpprest_find_winhttppal) + if(TARGET cpprestsdk_winhttppal_internal) + return() + endif() + + if(NOT WINHTTPPAL_LIBRARY OR NOT WINHTTPPAL_INCLUDE_DIRS) + find_package(winhttppal REQUIRED) + endif() + + add_library(cpprestsdk_winhttppal_internal INTERFACE) + if(TARGET winhttppal::winhttppal) + target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE winhttppal::winhttppal) + else() + target_link_libraries(cpprestsdk_winhttppal_internal INTERFACE "$") + target_include_directories(cpprestsdk_winhttppal_internal INTERFACE "$") + endif() +endfunction() diff --git a/Release/cmake/cpprestsdk-config-version.in.cmake b/Release/cmake/cpprestsdk-config-version.in.cmake new file mode 100644 index 0000000000..017879c58f --- /dev/null +++ b/Release/cmake/cpprestsdk-config-version.in.cmake @@ -0,0 +1,10 @@ +set(PACKAGE_VERSION @CPPREST_VERSION_MAJOR@.@CPPREST_VERSION_MINOR@.@CPPREST_VERSION_REVISION@) + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) +endif(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) diff --git a/Release/cmake/cpprestsdk-config.in.cmake b/Release/cmake/cpprestsdk-config.in.cmake index 8b5e8a6ff3..72476b062d 100644 --- a/Release/cmake/cpprestsdk-config.in.cmake +++ b/Release/cmake/cpprestsdk-config.in.cmake @@ -11,7 +11,11 @@ if(@CPPREST_USES_OPENSSL@) find_dependency(OpenSSL) endif() -if(@CPPREST_USES_BOOST@ AND OFF) +if(@CPPREST_USES_WINHTTPPAL@) + find_dependency(WINHTTPPAL) +endif() + +if(@CPPREST_USES_BOOST@) if(UNIX) find_dependency(Boost COMPONENTS random system thread filesystem chrono atomic date_time regex) else() diff --git a/Release/include/cpprest/asyncrt_utils.h b/Release/include/cpprest/asyncrt_utils.h index a38702f412..3e4bfdd5c4 100644 --- a/Release/include/cpprest/asyncrt_utils.h +++ b/Release/include/cpprest/asyncrt_utils.h @@ -373,6 +373,26 @@ inline bool __cdecl is_alnum(Elem ch) CPPREST_NOEXCEPT return (uch <= static_cast('z') && is_alnum(static_cast(uch))); } +/// +/// Our own implementation of whitespace test instead of std::isspace to avoid +/// taking global lock for performance reasons. +/// The following characters are considered whitespace: +/// 0x09 == Horizontal Tab +/// 0x0A == Line Feed +/// 0x0B == Vertical Tab +/// 0x0C == Form Feed +/// 0x0D == Carrage Return +/// 0x20 == Space +/// +template +inline bool __cdecl is_space(Elem ch) CPPREST_NOEXCEPT +{ + // assumes 'x' == L'x' for the ASCII range + typedef typename std::make_unsigned::type UElem; + const auto uch = static_cast(ch); + return uch == 0x20u || (uch >= 0x09u && uch <= 0x0Du); +} + /// /// Simplistic implementation of make_unique. A better implementation would be based on variadic templates /// and therefore not be compatible with Dev10. @@ -583,14 +603,21 @@ class datetime } } - datetime() : m_interval(0) {} + datetime() : m_interval(0) { } /// - /// Creates datetime from a string representing time in UTC in RFC 1123 format. + /// Creates datetime from a string representing time in UTC in RFC 1123 or ISO 8601 format. /// /// Returns a datetime of zero if not successful. static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123); + /// + /// Creates datetime from a string representing time in UTC in RFC 1123 or ISO 8601 format. + /// + /// Returns datetime::maximum() if not successful. + static _ASYNCRTIMP datetime __cdecl from_string_maximum_error(const utility::string_t& timestring, + date_format format = RFC_1123); + /// /// Returns a string representation of the datetime. /// @@ -601,6 +628,8 @@ class datetime /// interval_type to_interval() const { return m_interval; } + static datetime from_interval(interval_type interval) { return datetime(interval); } + datetime operator-(interval_type value) const { return datetime(m_interval - value); } datetime operator+(interval_type value) const { return datetime(m_interval + value); } @@ -609,6 +638,14 @@ class datetime bool operator!=(const datetime& dt) const { return !(*this == dt); } + bool operator>(const datetime& dt) const { return this->m_interval > dt.m_interval; } + + bool operator<(const datetime& dt) const { return this->m_interval < dt.m_interval; } + + bool operator>=(const datetime& dt) const { return this->m_interval >= dt.m_interval; } + + bool operator<=(const datetime& dt) const { return this->m_interval <= dt.m_interval; } + static interval_type from_milliseconds(unsigned int milliseconds) { return milliseconds * _msTicks; } static interval_type from_seconds(unsigned int seconds) { return seconds * _secondTicks; } @@ -621,6 +658,8 @@ class datetime bool is_initialized() const { return m_interval != 0; } + static datetime maximum() { return datetime(static_cast(-1)); } + private: friend int operator-(datetime t1, datetime t2); @@ -631,7 +670,7 @@ class datetime static const interval_type _dayTicks = 24 * 60 * 60 * _secondTicks; // Private constructor. Use static methods to create an instance. - datetime(interval_type interval) : m_interval(interval) {} + datetime(interval_type interval) : m_interval(interval) { } // Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns. interval_type m_interval; diff --git a/Release/include/cpprest/base_uri.h b/Release/include/cpprest/base_uri.h index 7c6943119c..7e96b6c016 100644 --- a/Release/include/cpprest/base_uri.h +++ b/Release/include/cpprest/base_uri.h @@ -296,13 +296,14 @@ class uri /// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine. /// /// - /// Examples include "localhost", or ip addresses in the loopback range (127.0.0.0/24). + /// Examples include "localhost", or "127.0.0.1". The only URIs for which this method returns true are "127.0.0.1", and "localhost", + /// all other URIs return false /// /// true if this URI references the local host, false otherwise. bool is_host_loopback() const { return !is_empty() && - ((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0, 4) == _XPLATSTR("127."))); + ((host() == _XPLATSTR("localhost")) || (host() == _XPLATSTR("127.0.0.1"))); } /// diff --git a/Release/include/cpprest/details/SafeInt3.hpp b/Release/include/cpprest/details/SafeInt3.hpp index 0a9dbdd76a..e6276f949b 100644 --- a/Release/include/cpprest/details/SafeInt3.hpp +++ b/Release/include/cpprest/details/SafeInt3.hpp @@ -1574,7 +1574,7 @@ class SafeCastHelper } template - static void CastThrow(bool b, T& t) SAFEINT_CPP_THROW + static void CastThrow(T t, bool& b) SAFEINT_CPP_THROW { b = !!t; } @@ -6022,6 +6022,8 @@ class SafeInt m_int = (T)(b ? 1 : 0); } + constexpr SafeInt(const SafeInt& u) SAFEINT_CPP_THROW = default; + template SafeInt(const SafeInt& u) SAFEINT_CPP_THROW { diff --git a/Release/include/cpprest/details/cpprest_compat.h b/Release/include/cpprest/details/cpprest_compat.h index c671ce2fe5..bf10747987 100644 --- a/Release/include/cpprest/details/cpprest_compat.h +++ b/Release/include/cpprest/details/cpprest_compat.h @@ -24,8 +24,6 @@ #define CPPREST_CONSTEXPR const #endif // _MSC_VER >= 1900 -#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x) - #include #else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv @@ -38,7 +36,6 @@ { \ if (!(x)) __builtin_unreachable(); \ } while (false) -#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x #define CPPREST_NOEXCEPT noexcept #define CPPREST_CONSTEXPR constexpr @@ -74,12 +71,20 @@ #ifdef _NO_ASYNCRTIMP #define _ASYNCRTIMP +#define _ASYNCRTIMP_TYPEINFO #else // ^^^ _NO_ASYNCRTIMP ^^^ // vvv !_NO_ASYNCRTIMP vvv #ifdef _ASYNCRT_EXPORT #define _ASYNCRTIMP __declspec(dllexport) #else // ^^^ _ASYNCRT_EXPORT ^^^ // vvv !_ASYNCRT_EXPORT vvv #define _ASYNCRTIMP __declspec(dllimport) #endif // _ASYNCRT_EXPORT + +#if defined(_WIN32) +#define _ASYNCRTIMP_TYPEINFO +#else // ^^^ _WIN32 ^^^ // vvv !_WIN32 vvv +#define _ASYNCRTIMP_TYPEINFO __attribute__((visibility("default"))) +#endif // _WIN32 + #endif // _NO_ASYNCRTIMP #ifdef CASABLANCA_DEPRECATION_NO_WARNINGS diff --git a/Release/include/cpprest/details/http_constants.dat b/Release/include/cpprest/details/http_constants.dat index c3b1a53cb6..3deb24a146 100644 --- a/Release/include/cpprest/details/http_constants.dat +++ b/Release/include/cpprest/details/http_constants.dat @@ -190,6 +190,7 @@ DAT(expires_in, "expires_in") DAT(grant_type, "grant_type") DAT(redirect_uri, "redirect_uri") DAT(refresh_token, "refresh_token") +DAT(client_credentials, "client_credentials") DAT(response_type, "response_type") DAT(scope, "scope") DAT(state, "state") diff --git a/Release/include/cpprest/details/web_utilities.h b/Release/include/cpprest/details/web_utilities.h index 8b99d94aa2..853d7614b1 100644 --- a/Release/include/cpprest/details/web_utilities.h +++ b/Release/include/cpprest/details/web_utilities.h @@ -24,23 +24,24 @@ class zero_memory_deleter }; typedef std::unique_ptr<::utility::string_t, zero_memory_deleter> plaintext_string; -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) -#if defined(__cplusplus_winrt) +#ifdef _WIN32 +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +#ifdef __cplusplus_winrt class winrt_encryption { public: - winrt_encryption() {} + winrt_encryption() = default; _ASYNCRTIMP winrt_encryption(const std::wstring& data); _ASYNCRTIMP plaintext_string decrypt() const; private: ::pplx::task m_buffer; }; -#else +#else // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv class win32_encryption { public: - win32_encryption() {} + win32_encryption() = default; _ASYNCRTIMP win32_encryption(const std::wstring& data); _ASYNCRTIMP ~win32_encryption(); _ASYNCRTIMP plaintext_string decrypt() const; @@ -49,8 +50,9 @@ class win32_encryption std::vector m_buffer; size_t m_numCharacters; }; -#endif -#endif +#endif // __cplusplus_winrt +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA +#endif // _WIN32 } // namespace details /// @@ -89,7 +91,7 @@ class credentials "This API is deprecated for security reasons to avoid unnecessary password copies stored in plaintext.") utility::string_t password() const { -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return utility::string_t(*m_password.decrypt()); #else return m_password; @@ -105,7 +107,7 @@ class credentials details::plaintext_string _internal_decrypt() const { // Encryption APIs not supported on XP -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return m_password.decrypt(); #else return details::plaintext_string(new ::utility::string_t(m_password)); @@ -115,7 +117,7 @@ class credentials private: ::utility::string_t m_username; -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) +#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA #if defined(__cplusplus_winrt) details::winrt_encryption m_password; #else @@ -151,13 +153,13 @@ class web_proxy /// /// Constructs a proxy with the default settings. /// - web_proxy() : m_address(_XPLATSTR("")), m_mode(use_default_) {} + web_proxy() : m_address(), m_mode(use_default_) {} /// /// Creates a proxy with specified mode. /// /// Mode to use. - web_proxy(web_proxy_mode mode) : m_address(_XPLATSTR("")), m_mode(static_cast(mode)) {} + web_proxy(web_proxy_mode mode) : m_address(), m_mode(static_cast(mode)) {} /// /// Creates a proxy explicitly with provided address. diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index 743e2b5635..fb7c6067ab 100644 --- a/Release/include/cpprest/http_client.h +++ b/Release/include/cpprest/http_client.h @@ -54,7 +54,7 @@ typedef void* native_handle; #include #include -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA #include "cpprest/oauth1.h" #endif @@ -104,13 +104,15 @@ class http_client_config #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) , m_tlsext_sni_enabled(true) #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) , m_buffer_request(false) #endif + , m_max_redirects(10) + , m_https_to_http_redirects(false) { } -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA /// /// Get OAuth 1.0 configuration. /// @@ -262,7 +264,7 @@ class http_client_config void set_validate_certificates(bool validate_certs) { m_validate_certificates = validate_certs; } #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) /// /// Checks if request data buffering is turned on, the default is off. /// @@ -279,6 +281,38 @@ class http_client_config void set_buffer_request(bool buffer_request) { m_buffer_request = buffer_request; } #endif + /// + /// Get the maximum number of redirects to follow automatically. + /// A value of 0 indicates that no automatic redirection is performed. + /// + /// The maximum number of redirects to follow automatically. + /// This is a hint -- an implementation may enforce a lower value. + size_t max_redirects() const { return m_max_redirects; } + + /// + /// Set the maximum number of redirects to follow automatically. + /// A value of 0 indicates that no automatic redirection is performed. + /// + /// The maximum number of redirects to follow automatically. + /// This is a hint -- an implementation may enforce a lower value. + void set_max_redirects(size_t max_redirects) { m_max_redirects = max_redirects; } + + /// + /// Checks if HTTPS to HTTP redirects are automatically followed. + /// + /// True if HTTPS to HTTP redirects are automatically followed, false otherwise. + bool https_to_http_redirects() const { return m_https_to_http_redirects; } + + /// + /// Sets if HTTPS to HTTP redirects are automatically followed. + /// + /// True if HTTPS to HTTP redirects are to be automatically + /// followed, false otherwise. + void set_https_to_http_redirects(bool https_to_http_redirects) + { + m_https_to_http_redirects = https_to_http_redirects; + } + /// /// Sets a callback to enable custom setting of platform specific options. /// @@ -363,7 +397,7 @@ class http_client_config #endif private: -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA std::shared_ptr m_oauth1; #endif @@ -389,9 +423,12 @@ class http_client_config std::function m_ssl_context_callback; bool m_tlsext_sni_enabled; #endif -#if defined(_WIN32) && !defined(__cplusplus_winrt) +#if (defined(_WIN32) && !defined(__cplusplus_winrt)) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) bool m_buffer_request; #endif + + size_t m_max_redirects; + bool m_https_to_http_redirects; }; class http_pipeline; @@ -716,7 +753,7 @@ class http_client namespace details { -#if defined(_WIN32) +#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) extern const utility::char_t* get_with_body_err_msg; #endif diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h index 50f05ef213..55c0433c94 100644 --- a/Release/include/cpprest/http_msg.h +++ b/Release/include/cpprest/http_msg.h @@ -187,7 +187,7 @@ class header_names /// /// Represents an HTTP error. This class holds an error message and an optional error code. /// -class http_exception : public std::exception +class _ASYNCRTIMP_TYPEINFO http_exception : public std::exception { public: /// diff --git a/Release/include/cpprest/json.h b/Release/include/cpprest/json.h index 4095be50ea..9549f9b8d5 100644 --- a/Release/include/cpprest/json.h +++ b/Release/include/cpprest/json.h @@ -100,25 +100,37 @@ class value /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from - _ASYNCRTIMP value(int32_t value); + _ASYNCRTIMP value(int value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from - _ASYNCRTIMP value(uint32_t value); + _ASYNCRTIMP value(unsigned value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from - _ASYNCRTIMP value(int64_t value); + _ASYNCRTIMP value(long value); /// /// Constructor creating a JSON number value /// /// The C++ value to create a JSON value from - _ASYNCRTIMP value(uint64_t value); + _ASYNCRTIMP value(unsigned long value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(long long value); + + /// + /// Constructor creating a JSON number value + /// + /// The C++ value to create a JSON value from + _ASYNCRTIMP value(unsigned long long value); /// /// Constructor creating a JSON number value @@ -222,28 +234,42 @@ class value /// /// The C++ value to create a JSON value from /// A JSON number value - static _ASYNCRTIMP value __cdecl number(int32_t value); + static _ASYNCRTIMP value __cdecl number(int value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(unsigned value); + + /// + /// Creates a number value + /// + /// The C++ value to create a JSON value from + /// A JSON number value + static _ASYNCRTIMP value __cdecl number(long value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value - static _ASYNCRTIMP value __cdecl number(uint32_t value); + static _ASYNCRTIMP value __cdecl number(unsigned long value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value - static _ASYNCRTIMP value __cdecl number(int64_t value); + static _ASYNCRTIMP value __cdecl number(long long value); /// /// Creates a number value /// /// The C++ value to create a JSON value from /// A JSON number value - static _ASYNCRTIMP value __cdecl number(uint64_t value); + static _ASYNCRTIMP value __cdecl number(unsigned long long value); /// /// Creates a Boolean value @@ -390,17 +416,37 @@ class value /// /// Parses a string and construct a JSON value. /// - /// The C++ value to create a JSON value from, a C++ STL double-byte string + /// The C++ value to create a JSON value from, a C++ STL string of the + /// platform-native character width _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value); /// /// Attempts to parse a string and construct a JSON value. /// - /// The C++ value to create a JSON value from, a C++ STL double-byte string + /// The C++ value to create a JSON value from, a C++ STL string of the + /// platform-native character width /// If parsing fails, the error code is greater than 0 /// The parsed object. Returns web::json::value::null if failed _ASYNCRTIMP static value __cdecl parse(const utility::string_t& value, std::error_code& errorCode); +#ifdef _WIN32 + /// + /// Parses a string and construct a JSON value. + /// + /// The C++ value to create a JSON value from, a C++ STL string in + /// UTF8 format + _ASYNCRTIMP static value __cdecl parse(const std::string& value); + + /// + /// Attempts to parse a string and construct a JSON value. + /// + /// The C++ value to create a JSON value from, a C++ STL string in + /// UTF8 format + /// If parsing fails, the error code is greater than 0 + /// The parsed object. Returns web::json::value::null if failed + _ASYNCRTIMP static value __cdecl parse(const std::string& value, std::error_code& errorCode); +#endif + /// /// Serializes the current JSON value to a C++ string. /// @@ -918,7 +964,7 @@ class array msl::safeint3::SafeInt nMinSize(index); nMinSize += 1; msl::safeint3::SafeInt nlastSize(m_elements.size()); - if (nlastSize < nMinSize) m_elements.resize(nMinSize); + if (nlastSize < nMinSize) m_elements.resize((size_type)nMinSize); return m_elements[index]; } @@ -1198,10 +1244,12 @@ class number // convert to unsigned int64). This helps handling number objects e.g. comparing two numbers. number(double value) : m_value(value), m_type(double_type) {} - number(int32_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} - number(uint32_t value) : m_intval(value), m_type(unsigned_type) {} - number(int64_t value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} - number(uint64_t value) : m_uintval(value), m_type(unsigned_type) {} + number(int value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} + number(unsigned value) : m_intval(value), m_type(unsigned_type) {} + number(long value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} + number(unsigned long value) : m_uintval(value), m_type(unsigned_type) {} + number(long long value) : m_intval(value), m_type(value < 0 ? signed_type : unsigned_type) {} + number(unsigned long long value) : m_uintval(value), m_type(unsigned_type) {} public: /// @@ -1418,10 +1466,12 @@ class _Number : public _Value { public: _Number(double value) : m_number(value) {} - _Number(int32_t value) : m_number(value) {} - _Number(uint32_t value) : m_number(value) {} - _Number(int64_t value) : m_number(value) {} - _Number(uint64_t value) : m_number(value) {} + _Number(int value) : m_number(value) {} + _Number(unsigned value) : m_number(value) {} + _Number(long value) : m_number(value) {} + _Number(unsigned long value) : m_number(value) {} + _Number(long long value) : m_number(value) {} + _Number(unsigned long long value) : m_number(value) {} virtual std::unique_ptr<_Value> _copy_value() { return utility::details::make_unique<_Number>(*this); } diff --git a/Release/include/cpprest/oauth2.h b/Release/include/cpprest/oauth2.h index 693ebbe34c..b1ec324996 100644 --- a/Release/include/cpprest/oauth2.h +++ b/Release/include/cpprest/oauth2.h @@ -284,6 +284,21 @@ class oauth2_config return _request_token(ub); } + /// + /// Fetches an access token from the token endpoint using client credentials grant type. + /// The task creates an HTTP request to the token_endpoint() using + /// client authentication as the authorization grant. + /// See: http://tools.ietf.org/html/rfc6749#section-4.4 + /// + /// Task that fetches token(s) using client credentials. + pplx::task token_from_client_credentials() + { + uri_builder ub; + ub.append_query( + details::oauth2_strings::grant_type, details::oauth2_strings::client_credentials, false); + return _request_token(ub); + } + /// /// Returns enabled state of the configuration. /// The oauth2_handler will perform OAuth 2.0 authentication only if diff --git a/Release/include/cpprest/producerconsumerstream.h b/Release/include/cpprest/producerconsumerstream.h index bbdb2c1c85..3487c4606f 100644 --- a/Release/include/cpprest/producerconsumerstream.h +++ b/Release/include/cpprest/producerconsumerstream.h @@ -47,6 +47,7 @@ class basic_producer_consumer_buffer : public streams::details::streambuf_state_ /// basic_producer_consumer_buffer(size_t alloc_size) : streambuf_state_manager<_CharType>(std::ios_base::out | std::ios_base::in) + , m_mode(std::ios_base::in) , m_alloc_size(alloc_size) , m_allocBlock(nullptr) , m_total(0) @@ -583,7 +584,7 @@ class basic_producer_consumer_buffer : public streams::details::streambuf_state_ // If front block is not empty - we are done if (m_blocks.front()->rd_chars_left() > 0) break; - // The block has no more data to be read. Relase the block + // The block has no more data to be read. Release the block m_blocks.pop_front(); } } diff --git a/Release/include/cpprest/streams.h b/Release/include/cpprest/streams.h index bfeeee5885..b6c3864028 100644 --- a/Release/include/cpprest/streams.h +++ b/Release/include/cpprest/streams.h @@ -17,6 +17,7 @@ #include "cpprest/astreambuf.h" #include +#include namespace Concurrency { @@ -470,7 +471,7 @@ _UINT_TRAIT(unsigned long long, ULLONG_MIN, ULLONG_MAX) { \ typedef std::true_type _is_integral; \ typedef std::false_type _is_unsigned; \ - static const int64_t _min = std::numeric_limits<_t>::min(); \ + static const int64_t _min = (std::numeric_limits<_t>::min)(); \ static const int64_t _max = (std::numeric_limits<_t>::max)(); \ }; #define _UINT_TRAIT(_t) \ @@ -528,9 +529,12 @@ class type_parser public: static pplx::task parse(streams::streambuf buffer) { - typename _type_parser_integral_traits::_is_integral ii; - typename _type_parser_integral_traits::_is_unsigned ui; - return _parse(buffer, ii, ui); + typedef typename _type_parser_integral_traits::_is_integral ii; + typedef typename _type_parser_integral_traits::_is_unsigned ui; + + static_assert(ii::value || !ui::value, "type is not supported for extraction from a stream"); + + return _parse(buffer, ii {}, ui {}); } private: @@ -539,15 +543,6 @@ class type_parser _parse_floating_point(buffer); } - static pplx::task _parse(streams::streambuf, std::false_type, std::true_type) - { -#ifdef _WIN32 - static_assert(false, "type is not supported for extraction from a stream"); -#else - throw std::runtime_error("type is not supported for extraction from a stream"); -#endif - } - static pplx::task _parse(streams::streambuf buffer, std::true_type, std::false_type) { return type_parser::parse(buffer).then([](pplx::task op) -> T { @@ -1145,8 +1140,8 @@ pplx::task _type_parser_base::_parse_input(concurrency::st auto update = [=](pplx::task op) -> pplx::task { int_type ch = op.get(); if (ch == traits::eof()) return pplx::task_from_result(false); - bool accptd = accept_character(state, ch); - if (!accptd) return pplx::task_from_result(false); + bool accepted = accept_character(state, ch); + if (!accepted) return pplx::task_from_result(false); // We peeked earlier, so now we must advance the position. concurrency::streams::streambuf buf = buffer; return buf.bumpc().then([](int_type) { return true; }); @@ -1314,9 +1309,18 @@ struct _double_state template static std::string create_exception_message(int_type ch, bool exponent) { - std::ostringstream os; - os << "Invalid character '" << char(ch) << "'" << (exponent ? " in exponent" : ""); - return os.str(); + std::string result; + if (exponent) + { + result.assign("Invalid character 'X' in exponent"); + } + else + { + result.assign("Invalid character 'X'"); + } + + result[19] = static_cast(ch); + return result; } template @@ -1431,7 +1435,8 @@ static pplx::task _extract_result(std::shared_ptr<_double_stateexponent_number >= 0) { - result *= pow(FloatingPoint(10.0), state->exponent_number); + result *= static_cast( + std::pow(static_cast(10.0), static_cast(state->exponent_number))); #pragma push_macro("max") #undef max @@ -1444,7 +1449,8 @@ static pplx::task _extract_result(std::shared_ptr<_double_stateexponent_number); + result /= static_cast( + std::pow(static_cast(10.0), static_cast(-state->exponent_number))); if (!is_zero && result > -std::numeric_limits::denorm_min() && result < std::numeric_limits::denorm_min()) diff --git a/Release/include/cpprest/version.h b/Release/include/cpprest/version.h index af1a6c7c1d..3f86f141fb 100644 --- a/Release/include/cpprest/version.h +++ b/Release/include/cpprest/version.h @@ -5,6 +5,6 @@ */ #define CPPREST_VERSION_MINOR 10 #define CPPREST_VERSION_MAJOR 2 -#define CPPREST_VERSION_REVISION 13 +#define CPPREST_VERSION_REVISION 19 #define CPPREST_VERSION (CPPREST_VERSION_MAJOR * 100000 + CPPREST_VERSION_MINOR * 100 + CPPREST_VERSION_REVISION) diff --git a/Release/include/cpprest/ws_client.h b/Release/include/cpprest/ws_client.h index 9c202d7f73..af17bd6060 100644 --- a/Release/include/cpprest/ws_client.h +++ b/Release/include/cpprest/ws_client.h @@ -330,8 +330,7 @@ class websocket_client_callback_impl virtual pplx::task close() = 0; - virtual pplx::task close(websocket_close_status close_status, - const utility::string_t& close_reason = _XPLATSTR("")) = 0; + virtual pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason = {}) = 0; virtual void set_close_handler( const std::function& @@ -478,7 +477,7 @@ class websocket_client /// frame. While closing an established connection, an endpoint may indicate the /// reason for closure. An asynchronous operation that is completed the connection has been /// successfully closed. - pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR("")) + pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason = {}) { return m_client->callback_client()->close(close_status, close_reason); } @@ -566,7 +565,7 @@ class websocket_callback_client /// frame. While closing an established connection, an endpoint may indicate the /// reason for closure. An asynchronous operation that is completed the connection has been /// successfully closed. - pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR("")) + pplx::task close(websocket_close_status close_status, const utility::string_t& close_reason = {}) { return m_client->close(close_status, close_reason); } diff --git a/Release/include/cpprest/ws_msg.h b/Release/include/cpprest/ws_msg.h index 64cee7ae52..9b13a80849 100644 --- a/Release/include/cpprest/ws_msg.h +++ b/Release/include/cpprest/ws_msg.h @@ -60,10 +60,23 @@ class websocket_outgoing_message public: #if !defined(__cplusplus_winrt) /// - /// Sets a the outgoing message to be an unsolicited pong message. + /// Sets the outgoing message to be a ping message. /// This is useful when the client side wants to check whether the server is alive. /// - void set_pong_message() { this->set_message_pong(); } + /// UTF-8 String containing the optional ping message. + void set_ping_message(const std::string& data = {}) + { + this->set_message_ping(concurrency::streams::container_buffer(data)); + } + + /// + /// Sets the outgoing message to be an unsolicited pong message. + /// + /// UTF-8 String containing the optional pong message. + void set_pong_message(const std::string& data = {}) + { + this->set_message_pong(concurrency::streams::container_buffer(data)); + } #endif /// @@ -140,9 +153,14 @@ class websocket_outgoing_message const pplx::task_completion_event& body_sent() const { return m_body_sent; } #if !defined(__cplusplus_winrt) - void set_message_pong() + void set_message_ping(const concurrency::streams::container_buffer& buffer) + { + m_msg_type = websocket_message_type::ping; + m_length = static_cast(buffer.size()); + m_body = buffer; + } + void set_message_pong(const concurrency::streams::container_buffer& buffer) { - concurrency::streams::container_buffer buffer(""); m_msg_type = websocket_message_type::pong; m_length = static_cast(buffer.size()); m_body = buffer; diff --git a/Release/include/pplx/pplxlinux.h b/Release/include/pplx/pplxlinux.h index 5aca316e45..ab0c5c9908 100644 --- a/Release/include/pplx/pplxlinux.h +++ b/Release/include/pplx/pplxlinux.h @@ -23,25 +23,14 @@ #include "pthread.h" #include -#if defined(__APPLE__) -#include -#include -#include -#else #include #include #include -#endif #include "pplx/pplxinterface.h" namespace pplx { -#if defined(__APPLE__) -namespace cpprest_synchronization = ::boost; -#else -namespace cpprest_synchronization = ::std; -#endif namespace details { namespace platform @@ -68,8 +57,8 @@ __declspec(noinline) inline static size_t CaptureCallstack(void**, size_t, size_ class event_impl { private: - cpprest_synchronization::mutex _lock; - cpprest_synchronization::condition_variable _condition; + std::mutex _lock; + std::condition_variable _condition; bool _signaled; public: @@ -79,20 +68,20 @@ class event_impl void set() { - cpprest_synchronization::lock_guard lock(_lock); + std::lock_guard lock(_lock); _signaled = true; _condition.notify_all(); } void reset() { - cpprest_synchronization::lock_guard lock(_lock); + std::lock_guard lock(_lock); _signaled = false; } unsigned int wait(unsigned int timeout) { - cpprest_synchronization::unique_lock lock(_lock); + std::unique_lock lock(_lock); if (timeout == event_impl::timeout_infinite) { _condition.wait(lock, [this]() -> bool { return _signaled; }); @@ -100,7 +89,7 @@ class event_impl } else { - cpprest_synchronization::chrono::milliseconds period(timeout); + std::chrono::milliseconds period(timeout); auto status = _condition.wait_for(lock, period, [this]() -> bool { return _signaled; }); _ASSERTE(status == _signaled); // Return 0 if the wait completed as a result of signaling the event. Otherwise, return timeout_infinite @@ -195,7 +184,7 @@ class recursive_lock_impl } private: - cpprest_synchronization::mutex _M_cs; + std::mutex _M_cs; std::atomic _M_owner; long _M_recursionCount; }; @@ -219,7 +208,7 @@ class linux_scheduler : public pplx::scheduler_interface /// /// A generic RAII wrapper for locks that implements the critical_section interface -/// cpprest_synchronization::lock_guard +/// std::lock_guard /// template class scoped_lock @@ -244,7 +233,7 @@ namespace extensibility { typedef ::pplx::details::event_impl event_t; -typedef cpprest_synchronization::mutex critical_section_t; +typedef std::mutex critical_section_t; typedef scoped_lock scoped_critical_section_t; typedef ::pplx::details::reader_writer_lock_impl reader_writer_lock_t; diff --git a/Release/include/pplx/pplxtasks.h b/Release/include/pplx/pplxtasks.h index 63cedc4f28..6868fc1619 100644 --- a/Release/include/pplx/pplxtasks.h +++ b/Release/include/pplx/pplxtasks.h @@ -3799,7 +3799,7 @@ class task auto _LogWorkItemAndInvokeUserLambda(_Func&& _func) const -> decltype(_func()) { details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); - CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); + (void)_LogWorkItem; return _func(); } @@ -3931,7 +3931,7 @@ class task -> decltype(_func(std::forward<_Arg>(_value))) { details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); - CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); + (void)_LogWorkItem; return _func(std::forward<_Arg>(_value)); } diff --git a/Release/libs/websocketpp b/Release/libs/websocketpp index c6d7e295bf..56123c8759 160000 --- a/Release/libs/websocketpp +++ b/Release/libs/websocketpp @@ -1 +1 @@ -Subproject commit c6d7e295bf5a0ab9b5f896720cc1a0e0fdc397a7 +Subproject commit 56123c87598f8b1dd471be83ca841ceae07f95ba diff --git a/Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp b/Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp index 5c9b1fc4b1..184ab42937 100644 --- a/Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp +++ b/Release/samples/BlackJack/BlackJack_Client/BlackJackClient.cpp @@ -17,7 +17,6 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif -#define NOMINMAX #include #include diff --git a/Release/samples/BlackJack/BlackJack_Server/CMakeLists.txt b/Release/samples/BlackJack/BlackJack_Server/CMakeLists.txt index cddfdc50a8..25d82598bc 100644 --- a/Release/samples/BlackJack/BlackJack_Server/CMakeLists.txt +++ b/Release/samples/BlackJack/BlackJack_Server/CMakeLists.txt @@ -10,13 +10,4 @@ add_executable(blackjackserver target_link_libraries(blackjackserver cpprest) -if(MSVC) - get_target_property(_srcs blackjackserver SOURCES) - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/blackjack-server-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/blackjack-server-stdafx.pch") - endif() - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fpblackjack-server-stdafx.pch /Zm120") - target_sources(blackjackserver PRIVATE stdafx.cpp) - target_compile_options(blackjackserver PRIVATE /Yustdafx.h /Fpblackjack-server-stdafx.pch /Zm120) -endif() +configure_pch(blackjackserver stdafx.h stdafx.cpp /Zm120) diff --git a/Release/samples/BlackJack/BlackJack_Server/messagetypes.h b/Release/samples/BlackJack/BlackJack_Server/messagetypes.h index 91c633984e..f38556dde3 100644 --- a/Release/samples/BlackJack/BlackJack_Server/messagetypes.h +++ b/Release/samples/BlackJack/BlackJack_Server/messagetypes.h @@ -179,7 +179,7 @@ struct BJHand { if (iter->value == CV_Ace) hasAces = true; - res.low += std::min((int)iter->value, 10); + res.low += (std::min)((int)iter->value, 10); } res.high = hasAces ? res.low + 10 : res.low; return res; @@ -271,7 +271,7 @@ struct Player static Player FromJSON(const web::json::object& object) { - Player result(U("")); + Player result(utility::string_t{}); auto iName = object.find(NAME); if (iName == object.end()) diff --git a/Release/samples/BlackJack/BlackJack_Server/stdafx.h b/Release/samples/BlackJack/BlackJack_Server/stdafx.h index ae59ac7dbd..d9bef583ff 100644 --- a/Release/samples/BlackJack/BlackJack_Server/stdafx.h +++ b/Release/samples/BlackJack/BlackJack_Server/stdafx.h @@ -26,9 +26,6 @@ #include #ifdef _WIN32 -#ifndef NOMINMAX -#define NOMINMAX -#endif #include #else #include diff --git a/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient140.vcxproj b/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient140.vcxproj deleted file mode 100644 index 2a0c920c4b..0000000000 --- a/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient140.vcxproj +++ /dev/null @@ -1,181 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {b8d3f85b-da71-4aca-87ba-10fed681dc79} - BlackJack_UIClient - en-US - 12.0 - true - SAK - SAK - SAK - SAK - Windows Store - 8.1 - - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - - - - - - - - - - - - - - - - - - - - - - - - BlackJack_UIClient_TemporaryKey.pfx - - - - pch.h - WIN32;%(PreprocessorDefinitions) - $(CasablancaIncludeDir);%(AdditionalIncludeDirectories) - 4100;4267;4450;4453;4702;%(DisableSpecificWarnings) - - - - $(OutDir)\$(MSBuildProjectName)\ - 5C945ED108BF1723994A90F9BAA4B7D93FBE0E47 - - - - CardShape.xaml - - - - App.xaml - - - Player.xaml - - - PlayingTable.xaml - - - - - Designer - - - - Designer - - - - - - - Designer - - - - - - - - - - - - - App.xaml - - - CardShape.xaml - - - Create - Create - Create - Create - Create - Create - - - Player.xaml - - - PlayingTable.xaml - - - - - {198ED804-2655-4D92-8104-C220E3EA9452} - - - - \ No newline at end of file diff --git a/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient141.vcxproj b/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient141.vcxproj deleted file mode 100644 index 9fd3069aa1..0000000000 --- a/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient141.vcxproj +++ /dev/null @@ -1,191 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {b8d3f85b-da71-4aca-87ba-10fed681dc79} - BlackJack_UIClient - en-US - 14.0 - true - SAK - SAK - SAK - SAK - Windows Store - 10.0 - 10.0.16299.0 - 10.0.16299.0 - - - - Application - true - v141 - - - Application - true - v141 - - - Application - true - v141 - - - Application - false - true - v141 - - - Application - false - true - v141 - - - Application - false - true - v141 - - - - - - - - - - - - - - - - - - - - - - - - - - BlackJack_UIClient_TemporaryKey.pfx - - - - pch.h - WIN32;%(PreprocessorDefinitions) - $(CasablancaIncludeDir);%(AdditionalIncludeDirectories) - 4100;4267;4450;4453;4702;%(DisableSpecificWarnings) - /Zm150 %(AdditionalOptions) - /Zm150 %(AdditionalOptions) - /Zm150 %(AdditionalOptions) - /Zm150 %(AdditionalOptions) - /Zm150 %(AdditionalOptions) - /Zm150 %(AdditionalOptions) - - - - $(OutDir)\$(MSBuildProjectName)\ - 5C945ED108BF1723994A90F9BAA4B7D93FBE0E47 - - - - CardShape.xaml - - - - App.xaml - - - Player.xaml - - - PlayingTable.xaml - - - - - Designer - - - - Designer - - - - - - - - - - - - - - - - - App.xaml - - - CardShape.xaml - - - Create - Create - Create - Create - Create - Create - - - Player.xaml - - - PlayingTable.xaml - - - - - {36d79e79-7e9e-4b3a-88a3-9f9b295c80b9} - - - - - Designer - - - - \ No newline at end of file diff --git a/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient_TemporaryKey.pfx b/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient_TemporaryKey.pfx deleted file mode 100644 index 53921885f2..0000000000 Binary files a/Release/samples/BlackJack/BlackJack_UIClient/BlackJack_UIClient_TemporaryKey.pfx and /dev/null differ diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.cpp b/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.cpp index e32a766883..394165a53c 100644 --- a/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.cpp +++ b/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.cpp @@ -54,7 +54,13 @@ void PlayerSpace::Update(Player player) playerName->Text = ref new Platform::String(web::uri::decode(player.Name).c_str()); playerBalance->Text = "$" + player.Balance.ToString(); playerBet->Text = "Bet: $" + player.Hand.bet.ToString(); - playerInsurance->Text = (player.Hand.insurance > 0) ? "Ins: $" + player.Hand.insurance.ToString() : ""; + if (player.Hand.insurance > 0) { + auto& text = playerInsurance->Text; + text.assign("Ins: $"); + text.append(std::to_string(player.Hand.insurance)); + } else { + text.clear(); + } } void PlayerSpace::AddCard(Card card) @@ -87,19 +93,19 @@ void PlayerSpace::ShowResult(BJHandResult result) { case BJHandResult::HR_ComputerWin: playerInsurance->Text = L"Dealer Wins"; - playerBet->Text = L""; + playerBet->Text.clear(); break; case BJHandResult::HR_PlayerWin: playerInsurance->Text = L"Player Wins"; - playerBet->Text = L""; + playerBet->Text.clear(); break; case BJHandResult::HR_Push: playerInsurance->Text = L"Push"; - playerBet->Text = L""; + playerBet->Text.clear(); break; case BJHandResult::HR_PlayerBlackJack: playerInsurance->Text = L"Blackjack!"; - playerBet->Text = L""; + playerBet->Text.clear(); break; default: break; } diff --git a/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.h b/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.h index 429d5e38c3..5b7ee7462b 100644 --- a/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.h +++ b/Release/samples/BlackJack/BlackJack_UIClient/Player.xaml.h @@ -25,10 +25,10 @@ ref class PlayerSpace sealed void Clear() { - playerBalance->Text = L""; - playerBet->Text = L""; - playerName->Text = L""; - playerInsurance->Text = L""; + playerBalance->Text.clear(); + playerBet->Text.clear(); + playerName->Text.clear(); + playerInsurance->Text.clear(); m_cards.clear(); playerCardGrid->Children->Clear(); } diff --git a/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.cpp b/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.cpp index 64fd0adae6..5f835e1b3f 100644 --- a/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.cpp +++ b/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.cpp @@ -116,7 +116,7 @@ void BlackjackClient::PlayingTable::Refresh() request.then( [this](pplx::task tsk) { - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -137,7 +137,7 @@ void BlackjackClient::PlayingTable::InterpretResponse(http_response& response) { if (response.headers().content_type() != L"application/json") return; - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); response.extract_json().then( [this, response](json::value jsonResponse) { @@ -265,7 +265,7 @@ void BlackjackClient::PlayingTable::BetButton_Click(Platform::Object ^ sender, request.then( [this](pplx::task tsk) { - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -307,7 +307,7 @@ void BlackjackClient::PlayingTable::InsuranceButton_Click(Platform::Object ^ sen request.then( [this](pplx::task tsk) { - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -343,7 +343,7 @@ void BlackjackClient::PlayingTable::DoubleButton_Click(Platform::Object ^ sender request.then( [this](pplx::task tsk) { - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -378,7 +378,7 @@ void BlackjackClient::PlayingTable::StayButton_Click(Platform::Object ^ sender, request.then( [this](pplx::task tsk) { - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -412,7 +412,7 @@ void BlackjackClient::PlayingTable::HitButton_Click(Platform::Object ^ sender, request.then( [this](pplx::task tsk) { - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -449,7 +449,7 @@ void BlackjackClient::PlayingTable::ExitButton_Click(Platform::Object ^ sender, [this](pplx::task tsk) { EnableExit(); - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -499,7 +499,7 @@ void BlackjackClient::PlayingTable::JoinButton_Click(Platform::Object ^ sender, [this](pplx::task tsk) { EnableExit(); - this->resultLabel->Text = L""; + this->resultLabel->Text.clear(); try { @@ -516,7 +516,7 @@ void BlackjackClient::PlayingTable::JoinButton_Click(Platform::Object ^ sender, { InterpretError(exc.error_code().value()); EnableConnecting(); - _tableId = L""; + _tableId.clear(); } }, ctx); @@ -551,7 +551,7 @@ void BlackjackClient::PlayingTable::LeaveButton_Click(Platform::Object ^ sender, this->resultLabel->Text = L"Thanks for playing!"; - _tableId = L""; + _tableId.clear(); } catch (const http_exception& exc) { diff --git a/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.h b/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.h index 589c1f9e6c..9f2fbe689a 100644 --- a/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.h +++ b/Release/samples/BlackJack/BlackJack_UIClient/PlayingTable.xaml.h @@ -93,7 +93,7 @@ ref class PlayingTable sealed { ClearDealerCards(); ClearPlayerCards(); - resultLabel->Text = ""; + resultLabel->Text.clear(); } void EnableBetting() diff --git a/Release/samples/CasaLens/CasaLens140/CasaLens140.vcxproj b/Release/samples/CasaLens/CasaLens140/CasaLens140.vcxproj deleted file mode 100644 index 5034ead2da..0000000000 --- a/Release/samples/CasaLens/CasaLens140/CasaLens140.vcxproj +++ /dev/null @@ -1,168 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {FFBFD6C1-B525-4D35-AB64-A2FE9460B147} - SAK - SAK - SAK - SAK - Win32Proj - $(VCTargetsPath12) - - - - Application - true - NotSet - v140 - - - Application - true - NotSet - v140 - - - Application - false - true - NotSet - v140 - - - Application - false - true - NotSet - v140 - - - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - true - true - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - true - true - - - - - - - - - - - - Create - Create - Create - Create - - - - - - - {1014c621-bc2d-4813-b8c1-6d83ad6f9249} - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Release/samples/CasaLens/CasaLens141/CasaLens141.vcxproj b/Release/samples/CasaLens/CasaLens141/CasaLens141.vcxproj deleted file mode 100644 index e43ab1b904..0000000000 --- a/Release/samples/CasaLens/CasaLens141/CasaLens141.vcxproj +++ /dev/null @@ -1,169 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {FFBFD6C1-B525-4D35-AB64-A2FE9460B147} - SAK - SAK - SAK - SAK - Win32Proj - $(VCTargetsPath12) - 10.0.16299.0 - - - - Application - true - NotSet - v141 - - - Application - true - NotSet - v141 - - - Application - false - true - NotSet - v141 - - - Application - false - true - NotSet - v141 - - - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - true - true - - - - - /bigobj -Zm140 %(AdditionalOptions) - Use - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(CasablancaIncludeDir) - Async - - - Console - true - true - true - - - - - - - - - - - - Create - Create - Create - Create - - - - - - - {1014c621-bc2d-4813-b8c1-6d83ad6f9249} - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Release/samples/CasaLens/casalens.cpp b/Release/samples/CasaLens/casalens.cpp index c98e926abf..5990ef240f 100644 --- a/Release/samples/CasaLens/casalens.cpp +++ b/Release/samples/CasaLens/casalens.cpp @@ -30,13 +30,13 @@ const utility::string_t casalens_creds::weather_url = U("http://api.openweatherm // FILL IN THE API KEYS FOR THE DIFFERENT SERVICES HERE. // Refer Readme.txt for details on how to obtain the key for the services. const utility::string_t casalens_creds::events_keyname = U("app_key"); -const utility::string_t casalens_creds::events_key = U(""); +const utility::string_t casalens_creds::events_key; const utility::string_t casalens_creds::movies_keyname = U("api_key"); -const utility::string_t casalens_creds::movies_key = U(""); +const utility::string_t casalens_creds::movies_key; const utility::string_t casalens_creds::images_keyname = U("username"); -const utility::string_t casalens_creds::images_key = U(""); +const utility::string_t casalens_creds::images_key; const utility::string_t casalens_creds::bmaps_keyname = U("key"); -const utility::string_t casalens_creds::bmaps_key = U(""); +const utility::string_t casalens_creds::bmaps_key; const utility::string_t CasaLens::events_json_key = U("events"); const utility::string_t CasaLens::movies_json_key = U("movies"); diff --git a/Release/samples/FacebookDemo/FacebookDemo140.vcxproj b/Release/samples/FacebookDemo/FacebookDemo140.vcxproj deleted file mode 100644 index bd249ff292..0000000000 --- a/Release/samples/FacebookDemo/FacebookDemo140.vcxproj +++ /dev/null @@ -1,182 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {43DE4DF3-ACAA-429E-B260-CC6D4FE82658} - FacebookDemo - en-US - 14.0 - true - Windows Store - 8.2 - - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - - - - - FacebookDemo_TemporaryKey.pfx - - - $(OutDir)\$(MSBuildProjectName)\ - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj /Zm137 %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - - - App.xaml - - - MainPage.xaml - - - - - Designer - - - Designer - - - Designer - - - - - Designer - - - - - - - - - - - - App.xaml - - - - MainPage.xaml - - - Create - Create - Create - Create - Create - Create - - - - - {198ed804-2655-4d92-8104-c220e3ea9452} - - - - - - \ No newline at end of file diff --git a/Release/samples/FacebookDemo/FacebookDemo141.vcxproj b/Release/samples/FacebookDemo/FacebookDemo141.vcxproj deleted file mode 100644 index fb8baf2f63..0000000000 --- a/Release/samples/FacebookDemo/FacebookDemo141.vcxproj +++ /dev/null @@ -1,184 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {43DE4DF3-ACAA-429E-B260-CC6D4FE82658} - FacebookDemo - en-US - 14.0 - true - Windows Store - 10.0.16299.0 - 10.0.16299.0 - 10.0 - - - - Application - true - v141 - - - Application - true - v141 - - - Application - true - v141 - - - Application - false - true - v141 - - - Application - false - true - v141 - - - Application - false - true - v141 - - - - - - - FacebookDemo_TemporaryKey.pfx - - - $(OutDir)\$(MSBuildProjectName)\ - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj /Zm137 %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - /bigobj %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - - - - App.xaml - - - MainPage.xaml - - - - - Designer - - - Designer - - - Designer - - - - - Designer - - - - - - - - - - - - App.xaml - - - - MainPage.xaml - - - Create - Create - Create - Create - Create - Create - - - - - {36d79e79-7e9e-4b3a-88a3-9f9b295c80b9} - - - - - - \ No newline at end of file diff --git a/Release/samples/FacebookDemo/FacebookDemo_TemporaryKey.pfx b/Release/samples/FacebookDemo/FacebookDemo_TemporaryKey.pfx deleted file mode 100644 index c02d448da8..0000000000 Binary files a/Release/samples/FacebookDemo/FacebookDemo_TemporaryKey.pfx and /dev/null differ diff --git a/Release/samples/OAuth2Live/MainPage.xaml.cpp b/Release/samples/OAuth2Live/MainPage.xaml.cpp index acdc7ba4e3..fef5d2d365 100644 --- a/Release/samples/OAuth2Live/MainPage.xaml.cpp +++ b/Release/samples/OAuth2Live/MainPage.xaml.cpp @@ -29,8 +29,8 @@ using namespace web::http::oauth2::experimental; // // NOTE: You must set this Live key and secret for app to work. // -static const utility::string_t s_live_key(U("")); -static const utility::string_t s_live_secret(U("")); +static const utility::string_t s_live_key; +static const utility::string_t s_live_secret; MainPage::MainPage() : m_live_oauth2_config(s_live_key, @@ -69,7 +69,7 @@ void OAuth2Live::MainPage::_GetToken() // Start over, clear tokens and button state. m_live_oauth2_config.set_token(oauth2_token()); - AccessToken->Text = ""; + AccessToken->Text.clear(); _UpdateButtonState(); String ^ authURI = ref new String(m_live_oauth2_config.build_authorization_uri(true).c_str()); diff --git a/Release/samples/OAuth2Live/OAuth2Live140.vcxproj b/Release/samples/OAuth2Live/OAuth2Live140.vcxproj deleted file mode 100644 index 6671f9c227..0000000000 --- a/Release/samples/OAuth2Live/OAuth2Live140.vcxproj +++ /dev/null @@ -1,188 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {2887A786-B818-4B3D-94EF-21EFD6AFDC22} - OAuth2Live - en-US - 14.0 - true - Windows Store - 8.2 - - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /bigobj /Zm200 %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - $(OutDir)\$(MSBuildProjectName)\ - - - OAuth2Live_TemporaryKey.pfx - - - - - App.xaml - - - MainPage.xaml - - - - - Designer - - - Designer - - - Designer - - - - - Designer - - - - - - - - - - - - App.xaml - - - MainPage.xaml - - - Create - Create - Create - Create - Create - Create - - - - - {198ed804-2655-4d92-8104-c220e3ea9452} - - - - - - \ No newline at end of file diff --git a/Release/samples/OAuth2Live/OAuth2Live141.vcxproj b/Release/samples/OAuth2Live/OAuth2Live141.vcxproj deleted file mode 100644 index 07d4d46897..0000000000 --- a/Release/samples/OAuth2Live/OAuth2Live141.vcxproj +++ /dev/null @@ -1,190 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {2887A786-B818-4B3D-94EF-21EFD6AFDC22} - OAuth2Live - en-US - 14.0 - true - Windows Store - 10.0.16299.0 - 10.0.16299.0 - 10.0 - - - - Application - true - v141 - - - Application - true - v141 - - - Application - true - v141 - - - Application - false - true - v141 - - - Application - false - true - v141 - - - Application - false - true - v141 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /bigobj /Zm200 %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - $(OutDir)\$(MSBuildProjectName)\ - - - OAuth2Live_TemporaryKey.pfx - - - - - App.xaml - - - MainPage.xaml - - - - - Designer - - - Designer - - - Designer - - - - - Designer - - - - - - - - - - - - App.xaml - - - MainPage.xaml - - - Create - Create - Create - Create - Create - Create - - - - - {36d79e79-7e9e-4b3a-88a3-9f9b295c80b9} - - - - - - \ No newline at end of file diff --git a/Release/samples/OAuth2Live/OAuth2Live_TemporaryKey.pfx b/Release/samples/OAuth2Live/OAuth2Live_TemporaryKey.pfx deleted file mode 100644 index c6d6c59f23..0000000000 Binary files a/Release/samples/OAuth2Live/OAuth2Live_TemporaryKey.pfx and /dev/null differ diff --git a/Release/samples/Oauth1Client/Oauth1Client.cpp b/Release/samples/Oauth1Client/Oauth1Client.cpp index da09fda29f..dec9b342b5 100644 --- a/Release/samples/Oauth1Client/Oauth1Client.cpp +++ b/Release/samples/Oauth1Client/Oauth1Client.cpp @@ -49,11 +49,11 @@ using namespace web::http::experimental::listener; // // Set key & secret pair to enable session for that service. // -static const utility::string_t s_linkedin_key(U("")); -static const utility::string_t s_linkedin_secret(U("")); +static const utility::string_t s_linkedin_key; +static const utility::string_t s_linkedin_secret; -static const utility::string_t s_twitter_key(U("")); -static const utility::string_t s_twitter_secret(U("")); +static const utility::string_t s_twitter_key; +static const utility::string_t s_twitter_secret; // // Utility method to open browser on Windows, OS X and Linux systems. diff --git a/Release/samples/Oauth2Client/Oauth2Client.cpp b/Release/samples/Oauth2Client/Oauth2Client.cpp index 2d82a70f18..19072cf6c6 100644 --- a/Release/samples/Oauth2Client/Oauth2Client.cpp +++ b/Release/samples/Oauth2Client/Oauth2Client.cpp @@ -49,14 +49,14 @@ using namespace web::http::experimental::listener; // // Set key & secret pair to enable session for that service. // -static const utility::string_t s_dropbox_key(U("")); -static const utility::string_t s_dropbox_secret(U("")); +static const utility::string_t s_dropbox_key; +static const utility::string_t s_dropbox_secret; -static const utility::string_t s_linkedin_key(U("")); -static const utility::string_t s_linkedin_secret(U("")); +static const utility::string_t s_linkedin_key; +static const utility::string_t s_linkedin_secret; -static const utility::string_t s_live_key(U("")); -static const utility::string_t s_live_secret(U("")); +static const utility::string_t s_live_key; +static const utility::string_t s_live_secret; // // Utility method to open browser on Windows, OS X and Linux systems. @@ -89,7 +89,7 @@ class oauth2_code_listener : m_listener(new http_listener(listen_uri)), m_config(config) { m_listener->support([this](http::http_request request) -> void { - if (request.request_uri().path() == U("/") && request.request_uri().query() != U("")) + if (request.request_uri().path() == U("/") && !request.request_uri().query().empty()) { m_resplock.lock(); diff --git a/Release/samples/Regenerate-Certificates.ps1 b/Release/samples/Regenerate-Certificates.ps1 deleted file mode 100644 index 96918fa99e..0000000000 --- a/Release/samples/Regenerate-Certificates.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -[CmdletBinding(SupportsShouldProcess=$true)] -param( - [Parameter(Mandatory=$true)] - [String]$PublisherName -) - -$ErrorActionPreference = "Stop" - -$Paths = @( - "$PSScriptRoot\BlackJack\BlackJack_UIClient\BlackJack_UIClient_TemporaryKey.pfx", - "$PSScriptRoot\FacebookDemo\FacebookDemo_TemporaryKey.pfx", - "$PSScriptRoot\OAuth2Live\OAuth2Live_TemporaryKey.pfx", - "$PSScriptRoot\WindowsLiveAuth\WindowsLiveAuth_TemporaryKey.pfx" -) - -$MakeCert = "C:\Program Files (x86)\Windows Kits\10\bin\x86\makecert.exe" -$Pvk2Pfx = "C:\Program Files (x86)\Windows Kits\10\bin\x86\pvk2pfx.exe" - -pushd $PSScriptRoot - -foreach($Path in $Paths) -{ - Remove-Item $Path -ErrorAction SilentlyContinue - if ($PSCmdlet.ShouldProcess($PSScriptRoot, "MakeCert")) - { - & $MakeCert -n "CN=$PublisherName" -a sha512 -m 12 -r -h 0 -eku "1.3.6.1.5.5.7.3.3,1.3.6.1.4.1.311.10.3.13" /sv "Temp.pvk" "Temp.cer" - } - if ($PSCmdlet.ShouldProcess($Path, "Pvk2Pfx")) - { - & $Pvk2Pfx -pvk "Temp.pvk" -spc "Temp.cer" -pfx $Path - Remove-Item "$PSScriptRoot\Temp.pvk" - Remove-Item "$PSScriptRoot\Temp.cer" - } -} diff --git a/Release/samples/WindowsLiveAuth/WindowsLiveAuth140.vcxproj b/Release/samples/WindowsLiveAuth/WindowsLiveAuth140.vcxproj deleted file mode 100644 index 7be6b6dda1..0000000000 --- a/Release/samples/WindowsLiveAuth/WindowsLiveAuth140.vcxproj +++ /dev/null @@ -1,189 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {1c20f771-3131-46e8-805f-aa1fe44165c0} - WindowsLiveAuth - en-US - 14.0 - true - Windows Store - 8.2 - - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /bigobj /Zm200 %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - $(OutDir)\$(MSBuildProjectName)\ - - - WindowsLiveAuth_TemporaryKey.pfx - - - - - - App.xaml - - - MainPage.xaml - - - - - Designer - - - Designer - - - Designer - - - - - Designer - - - - - - - - - - - - App.xaml - - - MainPage.xaml - - - Create - Create - Create - Create - Create - Create - - - - - {198ed804-2655-4d92-8104-c220e3ea9452} - - - - - - \ No newline at end of file diff --git a/Release/samples/WindowsLiveAuth/WindowsLiveAuth141.vcxproj b/Release/samples/WindowsLiveAuth/WindowsLiveAuth141.vcxproj deleted file mode 100644 index 7fec114667..0000000000 --- a/Release/samples/WindowsLiveAuth/WindowsLiveAuth141.vcxproj +++ /dev/null @@ -1,191 +0,0 @@ - - - - - Debug - ARM - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - Win32 - - - Release - x64 - - - - {1c20f771-3131-46e8-805f-aa1fe44165c0} - WindowsLiveAuth - en-US - 14.0 - true - Windows Store - 10.0.10240.0 - 10.0.10240.0 - 10.0 - - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - Application - false - true - v140 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /bigobj /Zm200 %(AdditionalOptions) - 4453 - $(MSBuildProjectDirectory);$(CasablancaIncludeDir);$(MSBuildProjectDirectory)\Generated Files;%(AdditionalIncludeDirectories) - - - - $(OutDir)\$(MSBuildProjectName)\ - - - WindowsLiveAuth_TemporaryKey.pfx - - - - - - App.xaml - - - MainPage.xaml - - - - - Designer - - - Designer - - - Designer - - - - - Designer - - - - - - - - - - - - App.xaml - - - MainPage.xaml - - - Create - Create - Create - Create - Create - Create - - - - - {36d79e79-7e9e-4b3a-88a3-9f9b295c80b9} - - - - - - \ No newline at end of file diff --git a/Release/samples/WindowsLiveAuth/WindowsLiveAuth_TemporaryKey.pfx b/Release/samples/WindowsLiveAuth/WindowsLiveAuth_TemporaryKey.pfx deleted file mode 100644 index 8cb613e1d5..0000000000 Binary files a/Release/samples/WindowsLiveAuth/WindowsLiveAuth_TemporaryKey.pfx and /dev/null differ diff --git a/Release/samples/WindowsLiveAuth/live_connect.h b/Release/samples/WindowsLiveAuth/live_connect.h index c8381df046..325538c84f 100644 --- a/Release/samples/WindowsLiveAuth/live_connect.h +++ b/Release/samples/WindowsLiveAuth/live_connect.h @@ -338,7 +338,7 @@ class live_client web::http::http_request req(web::http::methods::PUT); req.set_request_uri(bldr.to_string()); - req.set_body(stream, content_length, U("")); + req.set_body(stream, content_length, utility::string_t{}); return _make_request(req).then([](web::http::http_response response) { return _json_extract(response); }); } @@ -368,7 +368,7 @@ class live_client web::http::http_request req(web::http::methods::PUT); req.set_request_uri(bldr.to_string()); - req.set_body(stream, size, U("")); + req.set_body(stream, size, utility::string_t{}); return _make_request(req) .then([](web::http::http_response response) { return response.content_ready(); }) diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt index 89d2bc55c3..e15aeb7fc3 100644 --- a/Release/src/CMakeLists.txt +++ b/Release/src/CMakeLists.txt @@ -94,7 +94,7 @@ endif() if(CPPREST_PPLX_IMPL STREQUAL "apple") find_library(COREFOUNDATION CoreFoundation "/") find_library(SECURITY Security "/") - target_link_libraries(cpprest PUBLIC ${COREFOUNDATION} ${SECURITY}) + target_link_libraries(cpprest PRIVATE ${COREFOUNDATION} ${SECURITY}) target_sources(cpprest PRIVATE pplx/pplxapple.cpp pplx/pplx.cpp pplx/threadpool.cpp ../include/pplx/threadpool.h) if(CPPREST_INSTALL_HEADERS) install(FILES ../include/pplx/threadpool.h DESTINATION include/pplx) @@ -131,9 +131,15 @@ if(CPPREST_HTTP_CLIENT_IMPL STREQUAL "asio") target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_ASIO) target_sources(cpprest PRIVATE http/client/http_client_asio.cpp http/client/x509_cert_utilities.cpp) target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal) +elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttppal") + cpprest_find_boost() + cpprest_find_openssl() + cpprest_find_winhttppal() + target_compile_definitions(cpprest PUBLIC -DCPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) + target_sources(cpprest PRIVATE http/client/http_client_winhttp.cpp http/client/x509_cert_utilities.cpp) + target_link_libraries(cpprest PUBLIC cpprestsdk_boost_internal cpprestsdk_openssl_internal cpprestsdk_winhttppal_internal) elseif(CPPREST_HTTP_CLIENT_IMPL STREQUAL "winhttp") target_link_libraries(cpprest PRIVATE - httpapi.lib Winhttp.lib ) target_sources(cpprest PRIVATE http/client/http_client_winhttp.cpp) @@ -169,23 +175,15 @@ elseif(CPPREST_HTTP_LISTENER_IMPL STREQUAL "httpsys") http/listener/http_server_httpsys.cpp http/listener/http_server_httpsys.h ) + target_link_libraries(cpprest PRIVATE + httpapi.lib + ) elseif(CPPREST_HTTP_LISTENER_IMPL STREQUAL "none") else() message(FATAL_ERROR "Invalid implementation") endif() -if(MSVC) - get_target_property(_srcs cpprest SOURCES) - - 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 ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/stdafx.pch") - endif() - - set_source_files_properties(pch/stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Zm120") - target_sources(cpprest PRIVATE pch/stdafx.cpp) - target_compile_options(cpprest PRIVATE /Yustdafx.h /Zm120) -endif() +configure_pch(cpprest stdafx.h pch/stdafx.cpp /Zm120) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") if(WERROR) @@ -238,9 +236,9 @@ else() endif() if(CPPREST_INSTALL_HEADERS) - install(FILES ${HEADERS_CPPREST} DESTINATION include/cpprest) - install(FILES ${HEADERS_PPLX} DESTINATION include/pplx) - install(FILES ${HEADERS_DETAILS} DESTINATION include/cpprest/details) + install(FILES ${HEADERS_CPPREST} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpprest) + install(FILES ${HEADERS_PPLX} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pplx) + install(FILES ${HEADERS_DETAILS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpprest/details) endif() if(CPPREST_INSTALL) @@ -248,6 +246,7 @@ if(CPPREST_INSTALL) set(CPPREST_USES_ZLIB OFF) set(CPPREST_USES_BROTLI OFF) set(CPPREST_USES_OPENSSL OFF) + set(CPPREST_USES_WINHTTPPAL OFF) set(CPPREST_TARGETS cpprest) if(TARGET cpprestsdk_boost_internal) @@ -266,6 +265,10 @@ if(CPPREST_INSTALL) list(APPEND CPPREST_TARGETS cpprestsdk_openssl_internal) set(CPPREST_USES_OPENSSL ON) endif() + if(TARGET cpprestsdk_winhttppal_internal) + list(APPEND CPPREST_TARGETS cpprestsdk_winhttppal_internal) + set(CPPREST_USES_WINHTTPPAL ON) + endif() if(TARGET cpprestsdk_websocketpp_internal) list(APPEND CPPREST_TARGETS cpprestsdk_websocketpp_internal) endif() @@ -278,9 +281,10 @@ if(CPPREST_INSTALL) ) configure_file(../cmake/cpprestsdk-config.in.cmake "${CMAKE_CURRENT_BINARY_DIR}/cpprestsdk-config.cmake" @ONLY) + configure_file(../cmake/cpprestsdk-config-version.in.cmake "${CMAKE_CURRENT_BINARY_DIR}/cpprestsdk-config-version.cmake" @ONLY) install( - FILES "${CMAKE_CURRENT_BINARY_DIR}/cpprestsdk-config.cmake" + FILES "${CMAKE_CURRENT_BINARY_DIR}/cpprestsdk-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cpprestsdk-config-version.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/${CPPREST_EXPORT_DIR} ) install( diff --git a/Release/src/http/client/http_client.cpp b/Release/src/http/client/http_client.cpp index 41230ea517..09e3eed15a 100644 --- a/Release/src/http/client/http_client.cpp +++ b/Release/src/http/client/http_client.cpp @@ -41,8 +41,8 @@ static void verify_uri(const uri& uri) namespace details { -#if defined(_WIN32) -extern const utility::char_t* get_with_body_err_msg = +#if defined(_WIN32) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) +const utility::char_t* get_with_body_err_msg = _XPLATSTR("A GET or HEAD request should not have an entity body."); #endif @@ -386,7 +386,7 @@ http_client::http_client(const uri& base_uri, const http_client_config& client_c m_pipeline = std::make_shared(std::move(final_pipeline_stage)); -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA add_handler(std::static_pointer_cast( std::make_shared(client_config.oauth1()))); #endif diff --git a/Release/src/http/client/http_client_asio.cpp b/Release/src/http/client/http_client_asio.cpp index 1bcb335e27..07bb4885bf 100644 --- a/Release/src/http/client/http_client_asio.cpp +++ b/Release/src/http/client/http_client_asio.cpp @@ -87,8 +87,7 @@ namespace { const std::string CRLF("\r\n"); -std::string calc_cn_host(const web::http::uri& baseUri, - const web::http::http_headers& requestHeaders) +std::string calc_cn_host(const web::http::uri& baseUri, const web::http::http_headers& requestHeaders) { std::string result; if (baseUri.scheme() == U("https")) @@ -331,6 +330,13 @@ class asio_connection void start_reuse() { m_is_reused = true; } + void enable_no_delay() + { + boost::asio::ip::tcp::no_delay option(true); + boost::system::error_code error_ignored; + m_socket.set_option(option, error_ignored); + } + private: // Guards concurrent access to socket/ssl::stream. This is necessary // because timeouts and cancellation can touch the socket at the same time @@ -469,7 +475,6 @@ class asio_client final : public _http_client_communicator public: asio_client(http::uri&& address, http_client_config&& client_config) : _http_client_communicator(std::move(address), std::move(client_config)) - , m_resolver(crossplat::threadpool::shared_instance().service()) , m_pool(std::make_shared()) { } @@ -497,8 +502,6 @@ class asio_client final : public _http_client_communicator virtual pplx::task propagate(http_request request) override; - tcp::resolver m_resolver; - private: const std::shared_ptr m_pool; }; @@ -515,6 +518,7 @@ class asio_context final : public request_context, public std::enable_shared_fro , m_content_length(0) , m_needChunked(false) , m_timer(client->client_config().timeout()) + , m_resolver(crossplat::threadpool::shared_instance().service()) , m_connection(connection) #ifdef CPPREST_PLATFORM_ASIO_CERT_VERIFICATION_AVAILABLE , m_openssl_failed(false) @@ -570,7 +574,7 @@ class asio_context final : public request_context, public std::enable_shared_fro if (m_context->m_http_client->client_config().proxy().credentials().is_set()) { - request_stream << m_context->generate_basic_proxy_auth_header() << CRLF; + request_stream << m_context->generate_basic_proxy_auth_header(); } request_stream << CRLF; @@ -580,11 +584,11 @@ class asio_context final : public request_context, public std::enable_shared_fro tcp::resolver::query query(utility::conversions::to_utf8string(proxy_host), to_string(proxy_port)); auto client = std::static_pointer_cast(m_context->m_http_client); - client->m_resolver.async_resolve(query, - boost::bind(&ssl_proxy_tunnel::handle_resolve, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::iterator)); + m_context->m_resolver.async_resolve(query, + boost::bind(&ssl_proxy_tunnel::handle_resolve, + shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::iterator)); } private: @@ -611,6 +615,7 @@ class asio_context final : public request_context, public std::enable_shared_fro if (!ec) { m_context->m_timer.reset(); + m_context->m_connection->enable_no_delay(); m_context->m_connection->async_write(m_request, boost::bind(&ssl_proxy_tunnel::handle_write_request, shared_from_this(), @@ -881,12 +886,11 @@ class asio_context final : public request_context, public std::enable_shared_fro auto tcp_port = proxy_type == http_proxy_type::http ? proxy_port : port; tcp::resolver::query query(tcp_host, to_string(tcp_port)); - auto client = std::static_pointer_cast(ctx->m_http_client); - client->m_resolver.async_resolve(query, - boost::bind(&asio_context::handle_resolve, - ctx, - boost::asio::placeholders::error, - boost::asio::placeholders::iterator)); + ctx->m_resolver.async_resolve(query, + boost::bind(&asio_context::handle_resolve, + ctx, + boost::asio::placeholders::error, + boost::asio::placeholders::iterator)); } // Register for notification on cancellation to abort this request. @@ -905,7 +909,9 @@ class asio_context final : public request_context, public std::enable_shared_fro } }; - if (proxy_type == http_proxy_type::ssl_tunnel) + // Note that we must not try to CONNECT using an already established connection via proxy -- this would send + // CONNECT to the end server which is definitely not what we want. + if (proxy_type == http_proxy_type::ssl_tunnel && !m_connection->is_reused()) { // The ssl_tunnel_proxy keeps the context alive and then calls back once the ssl tunnel is established via // 'start_http_request_flow' @@ -1005,6 +1011,7 @@ class asio_context final : public request_context, public std::enable_shared_fro m_timer.reset(); if (!ec) { + m_connection->enable_no_delay(); write_request(); } else if (ec.value() == boost::system::errc::operation_canceled || @@ -1044,6 +1051,10 @@ class asio_context final : public request_context, public std::enable_shared_fro { report_error("Error resolving address", ec, httpclient_errorcode_context::connect); } + else if (endpoints == tcp::resolver::iterator()) + { + report_error("Failed to resolve address", ec, httpclient_errorcode_context::connect); + } else { m_timer.reset(); @@ -1233,8 +1244,8 @@ class asio_context final : public request_context, public std::enable_shared_fro } const auto this_request = shared_from_this(); - const auto readSize = static_cast( - std::min(static_cast(m_http_client->client_config().chunksize()), m_content_length - m_uploaded)); + const auto readSize = static_cast((std::min)( + static_cast(m_http_client->client_config().chunksize()), m_content_length - m_uploaded)); auto readbuf = _get_readbuffer(); readbuf.getn(boost::asio::buffer_cast(m_body_buf.prepare(readSize)), readSize) .then([this_request AND_CAPTURE_MEMBER_FUNCTION_POINTERS](pplx::task op) { @@ -1443,8 +1454,8 @@ class asio_context final : public request_context, public std::enable_shared_fro } } - m_content_length = std::numeric_limits::max(); // Without Content-Length header, size should be same as - // TCP stream - set it size_t max. + m_content_length = (std::numeric_limits::max)(); // Without Content-Length header, size should be same + // as TCP stream - set it size_t max. m_response.headers().match(header_names::content_length, m_content_length); if (!this->handle_compression()) @@ -1486,8 +1497,8 @@ class asio_context final : public request_context, public std::enable_shared_fro if (!needChunked) { async_read_until_buffersize( - static_cast( - std::min(m_content_length, static_cast(m_http_client->client_config().chunksize()))), + static_cast((std::min)(m_content_length, + static_cast(m_http_client->client_config().chunksize()))), boost::bind( &asio_context::handle_read_content, shared_from_this(), boost::asio::placeholders::error)); } @@ -1571,7 +1582,7 @@ class asio_context final : public request_context, public std::enable_shared_fro { if (inbytes) { - output.resize(output.size() + std::max(input_size, static_cast(1024))); + output.resize(output.size() + (std::max)(input_size, static_cast(1024))); } got = m_decompressor->decompress(input + inbytes, input_size - inbytes, @@ -1709,7 +1720,7 @@ class asio_context final : public request_context, public std::enable_shared_fro if (ec) { - if (ec == boost::asio::error::eof && m_content_length == std::numeric_limits::max()) + if (ec == boost::asio::error::eof && m_content_length == (std::numeric_limits::max)()) { m_content_length = m_downloaded + m_body_buf.size(); } @@ -1741,7 +1752,7 @@ class asio_context final : public request_context, public std::enable_shared_fro const auto this_request = shared_from_this(); auto read_size = static_cast( - std::min(static_cast(m_body_buf.size()), m_content_length - m_downloaded)); + (std::min)(static_cast(m_body_buf.size()), m_content_length - m_downloaded)); if (m_decompressor) { @@ -1764,7 +1775,7 @@ class asio_context final : public request_context, public std::enable_shared_fro this_request->m_downloaded += static_cast(read_size); this_request->async_read_until_buffersize( - static_cast(std::min( + static_cast((std::min)( static_cast(this_request->m_http_client->client_config().chunksize()), this_request->m_content_length - this_request->m_downloaded)), boost::bind( @@ -1793,7 +1804,7 @@ class asio_context final : public request_context, public std::enable_shared_fro this_request->m_downloaded += static_cast(read_size); this_request->m_body_buf.consume(read_size); this_request->async_read_until_buffersize( - static_cast(std::min( + static_cast((std::min)( static_cast(this_request->m_http_client->client_config().chunksize()), this_request->m_content_length - this_request->m_downloaded)), boost::bind(&asio_context::handle_read_content, @@ -1819,7 +1830,7 @@ class asio_context final : public request_context, public std::enable_shared_fro this_request->m_downloaded += static_cast(writtenSize); this_request->m_body_buf.consume(writtenSize); this_request->async_read_until_buffersize( - static_cast(std::min( + static_cast((std::min)( static_cast(this_request->m_http_client->client_config().chunksize()), this_request->m_content_length - this_request->m_downloaded)), boost::bind(&asio_context::handle_read_content, @@ -1927,6 +1938,7 @@ class asio_context final : public request_context, public std::enable_shared_fro uint64_t m_content_length; bool m_needChunked; timeout_timer m_timer; + tcp::resolver m_resolver; boost::asio::streambuf m_body_buf; std::shared_ptr m_connection; @@ -1965,6 +1977,162 @@ void asio_client::send_request(const std::shared_ptr& request_c ctx->start_request(); } +static bool is_retrieval_redirection(status_code code) +{ + // See https://tools.ietf.org/html/rfc7231#section-6.4 + + switch (code) + { + case status_codes::MovedPermanently: + // "For historical reasons, a user agent MAY change the request method + // from POST to GET for the subsequent request." + return true; + case status_codes::Found: + // "For historical reasons, a user agent MAY change the request method + // from POST to GET for the subsequent request." + return true; + case status_codes::SeeOther: + // "A user agent can perform a [GET or HEAD] request. It is primarily + // used to allow the output of a POST action to redirect the user agent + // to a selected resource." + return true; + default: + return false; + } +} + +static bool is_unchanged_redirection(status_code code) +{ + // See https://tools.ietf.org/html/rfc7231#section-6.4 + // and https://tools.ietf.org/html/rfc7538#section-3 + + switch (code) + { + case status_codes::TemporaryRedirect: + // "The user agent MUST NOT change the request method if it performs an + // automatic redirection to that URI." + return true; + case status_codes::PermanentRedirect: + // This status code "does not allow changing the request method from POST + // to GET." + return true; + default: + return false; + } +} + +static bool is_recognized_redirection(status_code code) +{ + // other 3xx status codes, e.g. 300 Multiple Choices, are not handled + // and should be handled externally + return is_retrieval_redirection(code) || is_unchanged_redirection(code); +} + +static bool is_retrieval_request(method method) +{ + return methods::GET == method || methods::HEAD == method; +} + +static const std::vector request_body_header_names = +{ + header_names::content_encoding, + header_names::content_language, + header_names::content_length, + header_names::content_location, + header_names::content_type +}; + +// A request continuation that follows redirects according to the specified configuration. +// This implementation only supports retrieval redirects, as it cannot redirect e.g. a POST request +// using the same method since the request body may have been consumed. +struct http_redirect_follower +{ + http_client_config config; + std::vector followed_urls; + http_request redirect; + + http_redirect_follower(http_client_config config, const http_request& request); + + uri url_to_follow(const http_response& response) const; + + pplx::task operator()(http_response response); +}; + +http_redirect_follower::http_redirect_follower(http_client_config config, const http_request& request) + : config(std::move(config)) + , followed_urls(1, request.absolute_uri()) + , redirect(request.method()) +{ + // Stash the original request URL, etc. to be prepared for an automatic redirect + + // Basically, it makes sense to send the redirects with the same headers as the original request + redirect.headers() = request.headers(); + // However, this implementation only supports retrieval redirects, with no body, so Content-* headers + // should be removed + for (const auto& content_header : request_body_header_names) + { + redirect.headers().remove(content_header); + } + + redirect._set_cancellation_token(request._cancellation_token()); +} + +uri http_redirect_follower::url_to_follow(const http_response& response) const +{ + // Return immediately if the response is not a supported redirection + if (!is_recognized_redirection(response.status_code())) + return{}; + + // Although not required by RFC 7231, config may limit the number of automatic redirects + // (followed_urls includes the initial request URL, hence '<' here) + if (config.max_redirects() < followed_urls.size()) + return{}; + + // Can't very well automatically redirect if the server hasn't provided a Location + const auto location = response.headers().find(header_names::location); + if (response.headers().end() == location) + return{}; + + uri to_follow(followed_urls.back().resolve_uri(location->second)); + + // Config may prohibit automatic redirects from HTTPS to HTTP + if (!config.https_to_http_redirects() && followed_urls.back().scheme() == _XPLATSTR("https") + && to_follow.scheme() != _XPLATSTR("https")) + return{}; + + // "A client SHOULD detect and intervene in cyclical redirections." + if (followed_urls.end() != std::find(followed_urls.begin(), followed_urls.end(), to_follow)) + return{}; + + return to_follow; +} + +pplx::task http_redirect_follower::operator()(http_response response) +{ + // Return immediately if the response doesn't indicate a valid automatic redirect + uri to_follow = url_to_follow(response); + if (to_follow.is_empty()) + return pplx::task_from_result(response); + + // This implementation only supports retrieval redirects, as it cannot redirect e.g. a POST request + // using the same method since the request body may have been consumed. + if (!is_retrieval_request(redirect.method()) && !is_retrieval_redirection(response.status_code())) + return pplx::task_from_result(response); + + if (!is_retrieval_request(redirect.method())) + redirect.set_method(methods::GET); + + // If the reply to this request is also a redirect, we want visibility of that + auto config_no_redirects = config; + config_no_redirects.set_max_redirects(0); + http_client client(to_follow, config_no_redirects); + + // Stash the redirect request URL and make the request with the same continuation + auto request_task = client.request(redirect, redirect._cancellation_token()); + followed_urls.push_back(std::move(to_follow)); + return request_task.then(std::move(*this)); +} + pplx::task asio_client::propagate(http_request request) { auto self = std::static_pointer_cast<_http_client_communicator>(shared_from_this()); @@ -1984,7 +2152,9 @@ pplx::task asio_client::propagate(http_request request) // Asynchronously send the response with the HTTP client implementation. this->async_send_request(context); - return result_task; + return client_config().max_redirects() > 0 + ? result_task.then(http_redirect_follower(client_config(), request)) + : result_task; } } // namespace details } // namespace client diff --git a/Release/src/http/client/http_client_impl.h b/Release/src/http/client/http_client_impl.h index 384c9b2de6..d9e7d4829e 100644 --- a/Release/src/http/client/http_client_impl.h +++ b/Release/src/http/client/http_client_impl.h @@ -30,13 +30,10 @@ 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 - +void parse_headers_string(_Inout_z_ utility::char_t* headersStr, http_headers& headers); } // namespace details } // namespace http } // namespace web diff --git a/Release/src/http/client/http_client_winhttp.cpp b/Release/src/http/client/http_client_winhttp.cpp index 61a13abfb5..d6cdb5384a 100644 --- a/Release/src/http/client/http_client_winhttp.cpp +++ b/Release/src/http/client/http_client_winhttp.cpp @@ -15,12 +15,18 @@ #include "stdafx.h" #include "../common/x509_cert_utilities.h" +#include "../common/internal_http_helpers.h" #include "cpprest/http_headers.h" #include "http_client_impl.h" +#ifdef WIN32 #include +#endif +#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) +#include "winhttppal.h" +#endif #include -#ifndef CPPREST_TARGET_XP +#if _WIN32_WINNT && (_WIN32_WINNT >= _WIN32_WINNT_VISTA) #include #endif @@ -96,19 +102,7 @@ static http::status_code parse_status_code(HINTERNET request_handle) &buffer[0], &length, WINHTTP_NO_HEADER_INDEX); - return (unsigned short)_wtoi(buffer.c_str()); -} - -// 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); + return (unsigned short)stoi(buffer); } // Helper function to get the reason phrase from a WinHTTP response. @@ -126,14 +120,14 @@ static utility::string_t parse_reason_phrase(HINTERNET request_handle) &length, WINHTTP_NO_HEADER_INDEX); // WinHTTP reports back the wrong length, trim any null characters. - trim_nulls(phrase); + ::web::http::details::trim_nulls(phrase); return phrase; } /// /// Parses a string containing HTTP headers. /// -static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utf16char* headersStr, http_response& response) +static void parse_winhttp_headers(HINTERNET request_handle, _In_z_ utility::char_t* headersStr, http_response& response) { // Clear the header map for each new response; otherwise, the header values will be combined. response.headers().clear(); @@ -152,7 +146,7 @@ static std::string build_error_msg(unsigned long code, const std::string& locati msg.append(": "); msg.append(std::to_string(code)); msg.append(": "); - msg.append(utility::details::windows_category().message(code)); + msg.append(utility::details::platform_category().message(static_cast(code))); return msg; } @@ -170,6 +164,7 @@ static std::string build_error_msg(_In_ WINHTTP_ASYNC_RESULT* error_result) } } + class memory_holder { uint8_t* m_externalData; @@ -210,6 +205,24 @@ enum msg_body_type transfer_encoding_chunked }; +static DWORD WinHttpDefaultProxyConstant() CPPREST_NOEXCEPT +{ +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +#if _WIN32_WINNT < _WIN32_WINNT_WINBLUE + if (!IsWindows8Point1OrGreater()) + { + // Not Windows 8.1 or later, use the default proxy setting + return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + } +#endif // _WIN32_WINNT < _WIN32_WINNT_WINBLUE + + // Windows 8.1 or later, use the automatic proxy setting + return WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; +#else // ^^^ _WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ // vvv _WIN32_WINNT < _WIN32_WINNT_VISTA vvv + return WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA +} + // Additional information necessary to track a WinHTTP request. class winhttp_request_context final : public request_context { @@ -245,11 +258,13 @@ class winhttp_request_context final : public request_context HINTERNET m_request_handle; std::weak_ptr* - m_request_handle_context; // owned by m_request_handle to be delete'd by WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING + m_request_handle_context; // owned by m_request_handle to be deleted by WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING bool m_proxy_authentication_tried; bool m_server_authentication_tried; + size_t m_remaining_redirects; + msg_body_type m_bodyType; utility::size64_t m_remaining_to_write; @@ -282,7 +297,7 @@ class winhttp_request_context final : public request_context { } -#if defined(_MSC_VER) && _MSC_VER < 1900 +#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) compression_state(const compression_state&) = delete; compression_state(compression_state&& other) : m_buffer(std::move(other.m_buffer)) @@ -464,7 +479,7 @@ class winhttp_request_context final : public request_context if (m_bytes_remaining) { // We're at the offset of a chunk of consumable data; let the caller process it - l = std::min(m_bytes_remaining, buffer_size - n); + l = (std::min)(m_bytes_remaining, buffer_size - n); m_bytes_remaining -= l; if (!m_bytes_remaining) { @@ -545,6 +560,10 @@ class winhttp_request_context final : public request_context void on_send_request_validate_cn() { +#if defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) + // we do the validation inside curl + return; +#else if (m_customCnCheck.empty()) { // no custom validation selected; either we've delegated that to winhttp or @@ -640,6 +659,7 @@ class winhttp_request_context final : public request_context } m_cachedEncodedCert.assign(encodedFirst, encodedLast); +#endif } protected: @@ -659,13 +679,14 @@ class winhttp_request_context final : public request_context winhttp_request_context(const std::shared_ptr<_http_client_communicator>& client, const http_request& request) : request_context(client, request) , m_request_handle(nullptr) - , m_bodyType(no_body) - , m_startingPosition(std::char_traits::eof()) - , m_body_data() - , m_remaining_to_write(0) , m_proxy_authentication_tried(false) , m_server_authentication_tried(false) + , m_remaining_redirects(0) + , m_bodyType(no_body) + , m_remaining_to_write(0) + , m_startingPosition(std::char_traits::eof()) , m_readStream(request.body()) + , m_body_data() { } }; @@ -724,10 +745,10 @@ class winhttp_client final : public _http_client_communicator public: winhttp_client(http::uri address, http_client_config client_config) : _http_client_communicator(std::move(address), std::move(client_config)) - , m_secure(m_uri.scheme() == _XPLATSTR("https")) , m_opened(false) , m_hSession(nullptr) , m_hConnection(nullptr) + , m_secure(m_uri.scheme() == _XPLATSTR("https")) { } @@ -745,7 +766,7 @@ class winhttp_client final : public _http_client_communicator if (m_hSession != nullptr) { // Unregister the callback. - WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL); + WinHttpSetStatusCallback(m_hSession, nullptr, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0); WinHttpCloseHandle(m_hSession); } @@ -785,38 +806,30 @@ class winhttp_client final : public _http_client_communicator ie_proxy_config proxyIE; DWORD access_type; - LPCWSTR proxy_name; - LPCWSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS; + LPCTSTR proxy_name = WINHTTP_NO_PROXY_NAME; + LPCTSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS; + m_proxy_auto_config = false; utility::string_t proxy_str; http::uri uri; const auto& config = client_config(); - - if (config.proxy().is_disabled()) + const auto& proxy = config.proxy(); + if (proxy.is_default()) + { + access_type = WinHttpDefaultProxyConstant(); + } + else if (proxy.is_disabled()) { access_type = WINHTTP_ACCESS_TYPE_NO_PROXY; - proxy_name = WINHTTP_NO_PROXY_NAME; } - else if (config.proxy().is_default() || config.proxy().is_auto_discovery()) + else if (proxy.is_auto_discovery()) { - // Use the default WinHTTP proxy by default. - access_type = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; - proxy_name = WINHTTP_NO_PROXY_NAME; - -#ifdef CPPREST_TARGET_XP - if (config.proxy().is_auto_discovery()) + access_type = WinHttpDefaultProxyConstant(); + if (access_type != WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY) { + // Windows 8 or earlier, do proxy autodetection ourselves m_proxy_auto_config = true; - } -#else // ^^^ CPPREST_TARGET_XP ^^^ // vvv !CPPREST_TARGET_XP vvv - if (IsWindows8Point1OrGreater()) - { - // Windows 8.1 and newer supports automatic proxy discovery and auto-fallback to IE proxy settings - access_type = WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY; - } - else - { - // However, if it is not configured... + proxy_info proxyDefault; if (!WinHttpGetDefaultProxyConfiguration(&proxyDefault) || proxyDefault.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY) @@ -848,13 +861,7 @@ class winhttp_client final : public _http_client_communicator } } } - - if (config.proxy().is_auto_discovery()) - { - m_proxy_auto_config = true; - } } -#endif // CPPREST_TARGET_XP } else { @@ -888,12 +895,14 @@ class winhttp_client final : public _http_client_communicator return GetLastError(); } - // Set timeouts. - int milliseconds = static_cast(config.timeout().count()); - milliseconds = std::max(milliseconds, 1); - if (!WinHttpSetTimeouts(m_hSession, milliseconds, milliseconds, milliseconds, milliseconds)) { - return GetLastError(); + // Set timeouts. + const int milliseconds = + (std::max)(static_cast(config.timeout().count()), 1); + if (!WinHttpSetTimeouts(m_hSession, milliseconds, milliseconds, milliseconds, milliseconds)) + { + return GetLastError(); + } } if (config.guarantee_order()) @@ -907,19 +916,18 @@ class winhttp_client final : public _http_client_communicator } } - // Enable TLS 1.1 and 1.2 -#if !defined(CPPREST_TARGET_XP) - BOOL win32_result(FALSE); - - DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | - WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2); - win32_result = ::WinHttpSetOption( - m_hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols)); - if (FALSE == win32_result) { - return GetLastError(); - } + // Enable TLS 1.1 and 1.2 +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) + DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2); + if (!WinHttpSetOption( + m_hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols))) + { + return GetLastError(); + } #endif + } config._invoke_nativesessionhandle_options(m_hSession); @@ -928,7 +936,8 @@ class winhttp_client final : public _http_client_communicator WinHttpSetStatusCallback(m_hSession, &winhttp_client::completion_callback, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_HANDLES | - WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST, + WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST | + WINHTTP_CALLBACK_STATUS_REDIRECT, 0)) { return GetLastError(); @@ -972,11 +981,20 @@ class winhttp_client final : public _http_client_communicator proxy_info info; bool proxy_info_required = false; - if (m_proxy_auto_config) + const auto& method = msg.method(); + + // stop injection of headers via method + // resource should be ok, since it's been encoded + // and host won't resolve + if (!::web::http::details::validate_method(method)) { - WINHTTP_AUTOPROXY_OPTIONS autoproxy_options; - memset(&autoproxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS)); + request->report_exception(http_exception("The method string is invalid.")); + return; + } + if (m_proxy_auto_config) + { + WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {}; if (m_proxy_auto_config_url.empty()) { autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; @@ -1074,11 +1092,11 @@ class winhttp_client final : public _http_client_communicator if (client_config().validate_certificates()) { // if we are validating certificates, also turn on revocation checking - DWORD dwEnableSSLRevocOpt = WINHTTP_ENABLE_SSL_REVOCATION; + DWORD dwEnableSSLRevocationOpt = WINHTTP_ENABLE_SSL_REVOCATION; if (!WinHttpSetOption(winhttp_context->m_request_handle, WINHTTP_OPTION_ENABLE_FEATURE, - &dwEnableSSLRevocOpt, - sizeof(dwEnableSSLRevocOpt))) + &dwEnableSSLRevocationOpt, + sizeof(dwEnableSSLRevocationOpt))) { auto errorCode = GetLastError(); request->report_error(errorCode, build_error_msg(errorCode, "Error enabling SSL revocation check")); @@ -1115,6 +1133,63 @@ class winhttp_client final : public _http_client_communicator return; } +// WinHttpPAL does not currently provide these options +// See https://github.com/microsoft/WinHttpPAL/issues/1 +#if !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) + if (client_config().max_redirects() == 0) + { + // Disable auto redirects. + DWORD redirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_NEVER; + if (!WinHttpSetOption(winhttp_context->m_request_handle, + WINHTTP_OPTION_REDIRECT_POLICY, + &redirectPolicy, + sizeof(redirectPolicy))) + { + auto errorCode = GetLastError(); + request->report_error(errorCode, build_error_msg(errorCode, "Setting redirect policy")); + return; + } + // Note, using WINHTTP_OPTION_DISABLE_FEATURE with WINHTTP_DISABLE_REDIRECTS here doesn't seem to work. + } + else + { + // Set max auto redirects. + + // Add 1 to config value because WinHttp option counts the original request. + // And another 1 to enable the response (headers) of the rejected automatic redirect to be returned + // rather than reporting an error "WinHttpReceiveResponse: 12156: The HTTP redirect request failed". + DWORD maxRedirects = client_config().max_redirects() < MAXDWORD - 2 + ? static_cast(client_config().max_redirects() + 2) + : MAXDWORD; + // Therefore, effective max redirects + winhttp_context->m_remaining_redirects = maxRedirects - 2; + + if (!WinHttpSetOption(winhttp_context->m_request_handle, + WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS, + &maxRedirects, + sizeof(maxRedirects))) + { + auto errorCode = GetLastError(); + request->report_error(errorCode, build_error_msg(errorCode, "Setting max automatic redirects")); + return; + } + + // (Dis)allow HTTPS to HTTP redirects. + DWORD redirectPolicy = client_config().https_to_http_redirects() + ? WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS + : WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP; + if (!WinHttpSetOption(winhttp_context->m_request_handle, + WINHTTP_OPTION_REDIRECT_POLICY, + &redirectPolicy, + sizeof(redirectPolicy))) + { + auto errorCode = GetLastError(); + request->report_error(errorCode, build_error_msg(errorCode, "Setting redirect policy")); + return; + } + } +#endif + size_t content_length; try { @@ -1134,7 +1209,7 @@ class winhttp_client final : public _http_client_communicator } // There is a request body that needs to be transferred. - if (content_length == std::numeric_limits::max()) + if (content_length == (std::numeric_limits::max)()) { // The content length is not set and the application set a stream. This is an // indication that we will use transfer encoding chunked. We still want to @@ -1315,11 +1390,11 @@ class winhttp_client final : public _http_client_communicator chunk_size = p_request_context->m_body_data.size() - http::details::chunked_encoding::additional_encoding_space; } - else if (p_request_context->m_remaining_to_write != std::numeric_limits::max()) + else if (p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { // Choose a semi-intelligent size based on how much total data is left to compress - chunk_size = std::min(static_cast(p_request_context->m_remaining_to_write) + 128, - p_request_context->m_http_client->client_config().chunksize()); + chunk_size = (std::min)(static_cast(p_request_context->m_remaining_to_write) + 128, + p_request_context->m_http_client->client_config().chunksize()); } else { @@ -1331,8 +1406,8 @@ class winhttp_client final : public _http_client_communicator { // We're not compressing; use the smaller of the remaining data (if known) and the configured (or default) // chunk size - chunk_size = std::min(static_cast(p_request_context->m_remaining_to_write), - p_request_context->m_http_client->client_config().chunksize()); + chunk_size = (std::min)(static_cast(p_request_context->m_remaining_to_write), + p_request_context->m_http_client->client_config().chunksize()); } p_request_context->allocate_request_space( nullptr, chunk_size + http::details::chunked_encoding::additional_encoding_space); @@ -1370,7 +1445,7 @@ class winhttp_client final : public _http_client_communicator chunk_size + http::details::chunked_encoding::additional_encoding_space, bytes_read); - if (!compressor && p_request_context->m_remaining_to_write != std::numeric_limits::max()) + if (!compressor && p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { if (bytes_read == 0 && p_request_context->m_remaining_to_write) { @@ -1425,7 +1500,6 @@ class winhttp_client final : public _http_client_communicator { return pplx::task_from_exception(std::current_exception()); } - _ASSERTE(bytes_read >= 0); uint8_t* buffer = p_request_context->m_compression_state.m_acquired; if (buffer == nullptr) @@ -1465,7 +1539,7 @@ class winhttp_client final : public _http_client_communicator p_request_context->m_compression_state.m_bytes_read) { if (p_request_context->m_remaining_to_write && - p_request_context->m_remaining_to_write != std::numeric_limits::max()) + p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { // The stream ended earlier than we detected it should return pplx::task_from_exception(http_exception( @@ -1518,7 +1592,7 @@ class winhttp_client final : public _http_client_communicator p_request_context->m_compression_state.m_bytes_processed += r.input_bytes_processed; _ASSERTE(p_request_context->m_compression_state.m_bytes_processed <= p_request_context->m_compression_state.m_bytes_read); - if (p_request_context->m_remaining_to_write != std::numeric_limits::max()) + if (p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { _ASSERTE(p_request_context->m_remaining_to_write >= r.input_bytes_processed); p_request_context->m_remaining_to_write -= r.input_bytes_processed; @@ -1568,7 +1642,7 @@ class winhttp_client final : public _http_client_communicator return; } else if (p_request_context->m_remaining_to_write && - p_request_context->m_remaining_to_write != std::numeric_limits::max()) + p_request_context->m_remaining_to_write != (std::numeric_limits::max)()) { // Unexpected end-of-stream. p_request_context->report_error(GetLastError(), @@ -1586,8 +1660,8 @@ class winhttp_client final : public _http_client_communicator } else { - length = std::min(static_cast(p_request_context->m_remaining_to_write), - p_request_context->m_http_client->client_config().chunksize()); + length = (std::min)(static_cast(p_request_context->m_remaining_to_write), + p_request_context->m_http_client->client_config().chunksize()); if (p_request_context->m_compression_state.m_buffer.capacity() < length) { p_request_context->m_compression_state.m_buffer.reserve(length); @@ -1698,16 +1772,16 @@ class winhttp_client final : public _http_client_communicator } } - static std::wstring get_request_url(HINTERNET hRequestHandle) + static utility::string_t get_request_url(HINTERNET hRequestHandle) { - std::wstring url; + utility::string_t url; auto urlSize = static_cast(url.capacity()) * 2; // use initial small string optimization capacity for (;;) { - url.resize(urlSize / sizeof(wchar_t)); - if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], &urlSize)) + url.resize(urlSize / sizeof(utility::char_t)); + if (WinHttpQueryOption(hRequestHandle, WINHTTP_OPTION_URL, &url[0], (LPDWORD)&urlSize)) { - url.resize(wcslen(url.c_str())); + url.resize(url.length()); return url; } @@ -1842,7 +1916,7 @@ class winhttp_client final : public _http_client_communicator if (content_length > 0) { // There is a request body that needs to be transferred. - if (content_length == std::numeric_limits::max()) + if (content_length == (std::numeric_limits::max)()) { // The content length is unknown and the application set a stream. This is an // indication that we will need to chunk the data. @@ -1873,7 +1947,7 @@ class winhttp_client final : public _http_client_communicator static void CALLBACK completion_callback( HINTERNET hRequestHandle, DWORD_PTR context, DWORD statusCode, _In_ void* statusInfo, DWORD statusInfoLength) { - CASABLANCA_UNREFERENCED_PARAMETER(statusInfoLength); + (void)statusInfoLength; std::weak_ptr* p_weak_request_context = reinterpret_cast*>(context); @@ -1962,11 +2036,17 @@ class winhttp_client final : public _http_client_communicator } return; } - case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: p_request_context->on_send_request_validate_cn(); return; + case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: + { + p_request_context->on_send_request_validate_cn(); + return; + } case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: + { p_request_context->report_exception(web::http::http_exception( generate_security_failure_message(*reinterpret_cast(statusInfo)))); return; + } case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: { DWORD bytesWritten = *((DWORD*)statusInfo); @@ -2014,6 +2094,49 @@ class winhttp_client final : public _http_client_communicator } return; } + case WINHTTP_CALLBACK_STATUS_REDIRECT: + { + // Return and continue unless that's too many automatic redirects. + if (p_request_context->m_remaining_redirects > 0) + { + --p_request_context->m_remaining_redirects; + return; + } + + // First need to query to see what the headers size is. + DWORD headerBufferLength = 0; + query_header_length(hRequestHandle, WINHTTP_QUERY_RAW_HEADERS_CRLF, headerBufferLength); + + // Now allocate buffer for headers and query for them. + std::vector header_raw_buffer; + header_raw_buffer.resize(headerBufferLength); + utility::char_t* header_buffer = reinterpret_cast(&header_raw_buffer[0]); + if (!WinHttpQueryHeaders(hRequestHandle, + WINHTTP_QUERY_RAW_HEADERS_CRLF, + WINHTTP_HEADER_NAME_BY_INDEX, + header_buffer, + &headerBufferLength, + WINHTTP_NO_HEADER_INDEX)) + { + auto errorCode = GetLastError(); + p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryHeaders")); + return; + } + + http_response& response = p_request_context->m_response; + parse_winhttp_headers(hRequestHandle, header_buffer, response); + + // Signal that the headers are available. + p_request_context->complete_headers(); + + // The body of the message is unavailable in WINHTTP_CALLBACK_STATUS_REDIRECT. + p_request_context->allocate_request_space(nullptr, 0); + p_request_context->complete_request(0); + + // Cancel the WinHTTP operation by closing the handle. + p_request_context->cleanup(); + return; + } case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: { // First need to query to see what the headers size is. @@ -2023,7 +2146,7 @@ class winhttp_client final : public _http_client_communicator // Now allocate buffer for headers and query for them. std::vector header_raw_buffer; header_raw_buffer.resize(headerBufferLength); - utf16char* header_buffer = reinterpret_cast(&header_raw_buffer[0]); + utility::char_t* header_buffer = reinterpret_cast(&header_raw_buffer[0]); if (!WinHttpQueryHeaders(hRequestHandle, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, @@ -2033,7 +2156,6 @@ class winhttp_client final : public _http_client_communicator { auto errorCode = GetLastError(); p_request_context->report_error(errorCode, build_error_msg(errorCode, "WinHttpQueryHeaders")); - ; return; } @@ -2061,7 +2183,7 @@ class winhttp_client final : public _http_client_communicator !p_request_context->m_http_client->client_config().request_compressed_response()) { p_request_context->m_compression_state.m_chunk = - std::make_unique(); + ::utility::details::make_unique(); p_request_context->m_compression_state.m_chunked = true; } @@ -2208,8 +2330,8 @@ class winhttp_client final : public _http_client_communicator if (p_request_context->m_decompressor) { - size_t chunk_size = std::max(static_cast(bytesRead), - p_request_context->m_http_client->client_config().chunksize()); + size_t chunk_size = (std::max)(static_cast(bytesRead), + p_request_context->m_http_client->client_config().chunksize()); p_request_context->m_compression_state.m_bytes_read = static_cast(bytesRead); p_request_context->m_compression_state.m_chunk_bytes = 0; @@ -2396,24 +2518,23 @@ class winhttp_client final : public _http_client_communicator return keep_going(p_request_context.get()); }); }); - }) - .then([p_request_context](pplx::task op) { - try - { - bool ignored = op.get(); - } - catch (...) + }).then([p_request_context](pplx::task op) { + try + { + op.get(); + } + catch (...) + { + // We're only here to pick up any exception that may have been thrown, and to clean up + // if needed + if (p_request_context->m_compression_state.m_acquired) { - // We're only here to pick up any exception that may have been thrown, and to clean up - // if needed - if (p_request_context->m_compression_state.m_acquired) - { - p_request_context->_get_writebuffer().commit(0); - p_request_context->m_compression_state.m_acquired = nullptr; - } - p_request_context->report_exception(std::current_exception()); + p_request_context->_get_writebuffer().commit(0); + p_request_context->m_compression_state.m_acquired = nullptr; } - }); + p_request_context->report_exception(std::current_exception()); + } + }); } else { @@ -2476,6 +2597,7 @@ std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage( return std::make_shared(std::move(base_uri), std::move(client_config)); } + } // namespace details } // namespace client } // namespace http diff --git a/Release/src/http/client/http_client_winrt.cpp b/Release/src/http/client/http_client_winrt.cpp index 294227512d..a95d9b3431 100644 --- a/Release/src/http/client/http_client_winrt.cpp +++ b/Release/src/http/client/http_client_winrt.cpp @@ -188,7 +188,7 @@ class IRequestStream final { public: IRequestStream(const std::weak_ptr& context, - size_t read_length = std::numeric_limits::max()) + size_t read_length = (std::numeric_limits::max)()) : m_context(context), m_read_length(read_length) { // read_length is the initial length of the ISequentialStream that is available for read @@ -253,9 +253,9 @@ class IRequestStream final _In_ ULONG cb, _Out_opt_ ULONG* pcbWritten) { - CASABLANCA_UNREFERENCED_PARAMETER(pv); - CASABLANCA_UNREFERENCED_PARAMETER(cb); - CASABLANCA_UNREFERENCED_PARAMETER(pcbWritten); + (void)pv; + (void)cb; + (void)pcbWritten; return E_NOTIMPL; } @@ -345,9 +345,9 @@ class IResponseStream final _In_ ULONG cb, _Out_ ULONG* pcbRead) { - CASABLANCA_UNREFERENCED_PARAMETER(pv); - CASABLANCA_UNREFERENCED_PARAMETER(cb); - CASABLANCA_UNREFERENCED_PARAMETER(pcbRead); + (void)pv; + (void)cb; + (void)pcbRead; return E_NOTIMPL; } @@ -403,7 +403,7 @@ class winrt_client final : public _http_client_communicator } const size_t content_length = msg._get_impl()->_get_content_length(); - if (content_length == std::numeric_limits::max()) + if (content_length == (std::numeric_limits::max)()) { // IXHR2 does not allow transfer encoding chunked. So the user is expected to set the content length request->report_exception(http_exception(L"Content length is not specified in the http headers")); @@ -478,7 +478,7 @@ class winrt_client final : public _http_client_communicator // Set timeout. ULONGLONG timeout = static_cast(config.timeout().count()); - timeout = std::max(timeout, std::numeric_limits::min() + 1); + timeout = (std::max)(timeout, (std::numeric_limits::min)() + 1); hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_TIMEOUT, timeout); if (FAILED(hr)) { diff --git a/Release/src/http/common/http_compression.cpp b/Release/src/http/common/http_compression.cpp index a9cbc86e1d..1596ddca86 100644 --- a/Release/src/http/common/http_compression.cpp +++ b/Release/src/http/common/http_compression.cpp @@ -17,21 +17,16 @@ // it. CPPREST_EXCLUDE_BROTLI is set if we want to explicitly disable Brotli compression support. // CPPREST_EXCLUDE_WEBSOCKETS is a flag that now essentially means "no external dependencies". TODO: Rename -#if __APPLE__ -#include "TargetConditionals.h" -#if defined(TARGET_OS_MAC) -#if !defined(CPPREST_EXCLUDE_COMPRESSION) -#define CPPREST_HTTP_COMPRESSION -#endif // !defined(CPPREST_EXCLUDE_COMPRESSION) -#endif // defined(TARGET_OS_MAC) -#elif defined(_WIN32) && (!defined(WINAPI_FAMILY) || WINAPI_PARTITION_DESKTOP) #if !defined(CPPREST_EXCLUDE_WEBSOCKETS) && !defined(CPPREST_EXCLUDE_COMPRESSION) #define CPPREST_HTTP_COMPRESSION #endif // !defined(CPPREST_EXCLUDE_WEBSOCKETS) && !defined(CPPREST_EXCLUDE_COMPRESSION) -#endif #if defined(CPPREST_HTTP_COMPRESSION) #include +// zconf.h may define compress +#ifdef compress +#undef compress +#endif #if !defined(CPPREST_EXCLUDE_BROTLI) #define CPPREST_BROTLI_COMPRESSION #endif // CPPREST_EXCLUDE_BROTLI @@ -93,7 +88,7 @@ class zlib_compressor_base : public compress_provider #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-constant-compare" #endif // __clang__ - if (input_size > std::numeric_limits::max() || output_size > std::numeric_limits::max()) + if (input_size > (std::numeric_limits::max)() || output_size > (std::numeric_limits::max)()) #if defined(__clang__) #pragma clang diagnostic pop #endif // __clang__ @@ -194,7 +189,7 @@ class zlib_decompressor_base : public decompress_provider #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-constant-compare" #endif // __clang__ - if (input_size > std::numeric_limits::max() || output_size > std::numeric_limits::max()) + if (input_size > (std::numeric_limits::max)() || output_size > (std::numeric_limits::max)()) #if defined(__clang__) #pragma clang diagnostic pop #endif // __clang__ @@ -273,7 +268,7 @@ class gzip_compressor : public zlib_compressor_base class gzip_decompressor : public zlib_decompressor_base { public: - gzip_decompressor() : zlib_decompressor_base(16) // gzip auto-detect + gzip_decompressor() : zlib_decompressor_base(31) // 15 is MAX_WBITS in zconf.h; add 16 for gzip { } }; diff --git a/Release/src/http/common/http_msg.cpp b/Release/src/http/common/http_msg.cpp index 50332fd1e9..a3c51c62a1 100644 --- a/Release/src/http/common/http_msg.cpp +++ b/Release/src/http/common/http_msg.cpp @@ -225,27 +225,27 @@ utility::string_t flatten_http_headers(const http_headers& headers) return flattened_headers; } -#if defined(_WIN32) -void parse_headers_string(_Inout_z_ utf16char* headersStr, http_headers& headers) +void parse_headers_string(_Inout_z_ utility::char_t* headersStr, web::http::http_headers& headers) { - utf16char* context = nullptr; - utf16char* line = wcstok_s(headersStr, CRLF, &context); - while (line != nullptr) + utility::string_t str(headersStr); + std::size_t pos = str.find_first_of(_XPLATSTR("\r\n")); + std::size_t startpos = 0; + while (pos!=std::string::npos) { - const utility::string_t header_line(line); + const utility::string_t header_line(str, startpos, pos - startpos); const size_t colonIndex = header_line.find_first_of(_XPLATSTR(":")); if (colonIndex != utility::string_t::npos) { utility::string_t key = header_line.substr(0, colonIndex); utility::string_t value = header_line.substr(colonIndex + 1, header_line.length() - colonIndex - 1); - http::details::trim_whitespace(key); - http::details::trim_whitespace(value); + web::http::details::trim_whitespace(key); + web::http::details::trim_whitespace(value); headers.add(key, value); } - line = wcstok_s(nullptr, CRLF, &context); + startpos = pos + 1; + pos = str.find_first_of(_XPLATSTR("\r\n"), pos + 1); } } -#endif } // namespace details @@ -287,11 +287,7 @@ static const utility::char_t* stream_was_set_explicitly = 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."); -http_msg_base::http_msg_base() - : m_http_version(http::http_version{ 0, 0 }) - , m_headers() - , m_default_outstream(false) -{} +http_msg_base::http_msg_base() : m_http_version(http::http_version {0, 0}), m_headers(), m_default_outstream(false) {} void http_msg_base::_prepare_to_receive_data() { @@ -326,7 +322,7 @@ size_t http_msg_base::_get_stream_length() return static_cast(end - offset); } - return std::numeric_limits::max(); + return (std::numeric_limits::max)(); } size_t http_msg_base::_get_content_length(bool honor_compression) @@ -356,7 +352,7 @@ size_t http_msg_base::_get_content_length(bool honor_compression) } } - return std::numeric_limits::max(); + return (std::numeric_limits::max)(); } if (honor_compression && m_compressor) @@ -365,7 +361,7 @@ size_t http_msg_base::_get_content_length(bool honor_compression) // up front for content encoding. We return the uncompressed length if we can figure it out. headers().add(header_names::transfer_encoding, m_compressor->algorithm()); headers().add(header_names::transfer_encoding, _XPLATSTR("chunked")); - return std::numeric_limits::max(); + return (std::numeric_limits::max)(); } if (headers().match(header_names::content_length, content_length)) @@ -376,7 +372,7 @@ size_t http_msg_base::_get_content_length(bool honor_compression) } content_length = _get_stream_length(); - if (content_length != std::numeric_limits::max()) + if (content_length != (std::numeric_limits::max)()) { // The content length wasn't explicitly set, but we figured it out; // use it, since sending this way is more efficient than chunking @@ -386,7 +382,7 @@ size_t http_msg_base::_get_content_length(bool honor_compression) // We don't know the content length; we'll chunk the stream headers().add(header_names::transfer_encoding, _XPLATSTR("chunked")); - return std::numeric_limits::max(); + return (std::numeric_limits::max)(); } // There is no content diff --git a/Release/src/http/common/internal_http_helpers.h b/Release/src/http/common/internal_http_helpers.h index 2270f7b955..1fbdfdfa58 100644 --- a/Release/src/http/common/internal_http_helpers.h +++ b/Release/src/http/common/internal_http_helpers.h @@ -5,6 +5,7 @@ #pragma once +#include "cpprest/asyncrt_utils.h" #include "cpprest/details/basic_types.h" #include @@ -19,19 +20,71 @@ namespace details /// utility::string_t get_default_reason_phrase(status_code code); -// simple helper functions to trim whitespace. +template +void trim_if(std::basic_string& str, Fn test) +{ + if (str.empty()) + { + return; + } + + auto first = str.begin(); + auto last = str.end(); + + if (test(*first)) + { + // removals at the front, and maybe the back + for (;;) + { + ++first; + if (first == last) + { + // all removals + str.clear(); + return; + } + + if (!test(*first)) + { + break; + } + } + + do + { + --last; + } while (test(*last)); + ++last; + str.assign(first, last); + return; + } + + // no removals at the front, only maybe the back + --last; + if (!test(*last)) + { + // no removals at all + return; + } + + do + { + --last; + } while (test(*last)); + ++last; + str.erase(last, str.end()); +} + +template +void trim_nulls(std::basic_string& str) +{ + trim_if(str, [](const Char c) { return c == Char {}; }); +} + template void trim_whitespace(std::basic_string& str) { - size_t index; - // trim left whitespace - for (index = 0; index < str.size() && isspace(str[index]); ++index) - ; - str.erase(0, index); - // trim right whitespace - for (index = str.size(); index > 0 && isspace(str[index - 1]); --index) - ; - str.erase(index); + trim_if(str, [](const Char c) { return ::utility::details::is_space(c); }); } bool validate_method(const utility::string_t& method); diff --git a/Release/src/http/listener/http_server_api.cpp b/Release/src/http/listener/http_server_api.cpp index 8c50353783..ea985f4a0a 100644 --- a/Release/src/http/listener/http_server_api.cpp +++ b/Release/src/http/listener/http_server_api.cpp @@ -50,7 +50,7 @@ void http_server_api::unregister_server_api() throw http_exception(_XPLATSTR("Server API was cleared while listeners were still attached")); } - s_server_api.release(); + s_server_api.reset(); } void http_server_api::unsafe_register_server_api(std::unique_ptr server_api) diff --git a/Release/src/http/listener/http_server_asio.cpp b/Release/src/http/listener/http_server_asio.cpp index e35838a077..e83b9ff525 100644 --- a/Release/src/http/listener/http_server_asio.cpp +++ b/Release/src/http/listener/http_server_asio.cpp @@ -175,7 +175,7 @@ class hostport_listener auto path_segments = uri::split_path(uri::decode(u.path())); for (auto i = static_cast(path_segments.size()); i >= 0; --i) { - std::string path = ""; + std::string path; for (size_t j = 0; j < static_cast(i); ++j) { path += "/" + utility::conversions::to_utf8string(path_segments[j]); @@ -520,7 +520,9 @@ void hostport_listener::start() auto& service = crossplat::threadpool::shared_instance().service(); tcp::resolver resolver(service); // #446: boost resolver does not recognize "+" as a host wildchar - tcp::resolver::query query = ("+" == m_host) ? tcp::resolver::query(m_port) : tcp::resolver::query(m_host, m_port); + tcp::resolver::query query = + ("+" == m_host) ? tcp::resolver::query(m_port, boost::asio::ip::resolver_query_base::flags()) + : tcp::resolver::query(m_host, m_port, boost::asio::ip::resolver_query_base::flags()); tcp::endpoint endpoint = *resolver.resolve(query); @@ -591,6 +593,10 @@ void hostport_listener::on_accept(std::unique_ptr socket, const // Handle successful accept if (!ec) { + boost::asio::ip::tcp::no_delay option(true); + boost::system::error_code error_ignored; + socket->set_option(option, error_ignored); + auto conn = asio_server_connection::create(std::move(socket), m_p_server, this); m_connections.insert(conn.get()); @@ -826,7 +832,7 @@ will_deref_and_erase_t asio_server_connection::handle_headers() m_read = 0; ++m_refs; async_read_until_buffersize( - std::min(ChunkSize, m_read_size), + (std::min)(ChunkSize, m_read_size), [this](const boost::system::error_code& ec, size_t) { (will_deref_t) this->handle_body(ec); }); } @@ -908,7 +914,7 @@ will_deref_t asio_server_connection::handle_body(const boost::system::error_code auto writebuf = requestImpl->outstream().streambuf(); writebuf .putn_nocopy(boost::asio::buffer_cast(m_request_buf.data()), - std::min(m_request_buf.size(), m_read_size - m_read)) + (std::min)(m_request_buf.size(), m_read_size - m_read)) .then([this](pplx::task writtenSizeTask) -> will_deref_t { size_t writtenSize = 0; try @@ -924,7 +930,7 @@ will_deref_t asio_server_connection::handle_body(const boost::system::error_code m_request_buf.consume(writtenSize); async_read_until_buffersize( - std::min(ChunkSize, m_read_size - m_read), + (std::min)(ChunkSize, m_read_size - m_read), [this](const boost::system::error_code& ec, size_t) { (will_deref_t) this->handle_body(ec); }); return will_deref_t {}; }); @@ -1160,7 +1166,7 @@ will_deref_and_erase_t asio_server_connection::handle_write_large_response(const if (readbuf.is_eof()) return cancel_sending_response_with_error( response, std::make_exception_ptr(http_exception("Response stream close early!"))); - size_t readBytes = std::min(ChunkSize, m_write_size - m_write); + size_t readBytes = (std::min)(ChunkSize, m_write_size - m_write); readbuf.getn(buffer_cast(m_response_buf.prepare(readBytes)), readBytes) .then([=](pplx::task actualSizeTask) -> will_deref_and_erase_t { size_t actualSize = 0; diff --git a/Release/src/http/listener/http_server_httpsys.cpp b/Release/src/http/listener/http_server_httpsys.cpp index 49d83fe874..2eac0baf11 100644 --- a/Release/src/http/listener/http_server_httpsys.cpp +++ b/Release/src/http/listener/http_server_httpsys.cpp @@ -32,7 +32,7 @@ using namespace http::details; using namespace http::experimental::listener; using namespace http::experimental::details; -#define CHUNK_SIZE 64 * 1024 +#define CHUNK_SIZE (64 * 1024) namespace web { @@ -43,62 +43,53 @@ namespace experimental namespace details { /// -/// String values for all HTTP Server API known headers. +/// String values for all HTTP Server API HTTP_REQUEST_HEADERS known headers. /// NOTE: the order here is important it is from the _HTTP_HEADER_ID enum. /// -static utility::string_t HttpServerAPIKnownHeaders[] = {U("Cache-Control"), - U("Connection"), - U("Data"), - U("Keep-Alive"), - U("Pragma"), - U("Trailer"), - U("Transfer-Encoding"), - U("Upgrade"), - U("Via"), - U("Warning"), - U("Allow"), - U("Content-Length"), - U("Content-Type"), - U("Content-Encoding"), - U("Content-Language"), - U("Content-Location"), - U("Content-Md5"), - U("Content-Range"), - U("Expires"), - U("Last-Modified"), - U("Accept"), - U("Accept-Charset"), - U("Accept-Encoding"), - U("Accept-Language"), - U("Authorization"), - U("Cookie"), - U("Expect"), - U("From"), - U("Host"), - U("If-Match"), - U("If-Modified-Since"), - U("If-None-Match"), - U("If-Range"), - U("If-Unmodified-Since"), - U("Max-Forwards"), - U("Proxy-Authorization"), - U("Referer"), - U("Range"), - U("TE"), - U("Translate"), - U("User-Agent"), - U("Request-Maximum"), - U("Accept-Ranges"), - U("Age"), - U("Etag"), - U("Location"), - U("Proxy-Authenticate"), - U("Retry-After"), - U("Server"), - U("Set-Cookie"), - U("Vary"), - U("Www-Authenticate"), - U("Response-Maximum")}; +static utility::string_t HttpServerAPIRequestKnownHeaders[] = +{ + U("Cache-Control"), + U("Connection"), + U("Date"), + U("Keep-Alive"), + U("Pragma"), + U("Trailer"), + U("Transfer-Encoding"), + U("Upgrade"), + U("Via"), + U("Warning"), + U("Allow"), + U("Content-Length"), + U("Content-Type"), + U("Content-Encoding"), + U("Content-Language"), + U("Content-Location"), + U("Content-MD5"), + U("Content-Range"), + U("Expires"), + U("Last-Modified"), + U("Accept"), + U("Accept-Charset"), + U("Accept-Encoding"), + U("Accept-Language"), + U("Authorization"), + U("Cookie"), + U("Expect"), + U("From"), + U("Host"), + U("If-Match"), + U("If-Modified-Since"), + U("If-None-Match"), + U("If-Range"), + U("If-Unmodified-Since"), + U("Max-Forwards"), + U("Proxy-Authorization"), + U("Referer"), + U("Range"), + U("TE"), + U("Translate"), + U("User-Agent") +}; static void char_to_wstring(utf16string& dest, const char* src) { @@ -155,14 +146,14 @@ void parse_http_headers(const HTTP_REQUEST_HEADERS& headers, http::http_headers& } else { - msgHeaders[unknown_header_name] = U(""); + msgHeaders[unknown_header_name].clear(); } } for (int i = 0; i < HttpHeaderMaximum; ++i) { if (headers.KnownHeaders[i].RawValueLength > 0) { - msgHeaders.add(HttpServerAPIKnownHeaders[i], + msgHeaders.add(HttpServerAPIRequestKnownHeaders[i], utility::conversions::to_utf16string(headers.KnownHeaders[i].pRawValue)); } } @@ -976,10 +967,10 @@ void windows_request_context::async_process_response() // OK, so we need to chunk it up. _ASSERTE(content_length > 0); - m_sending_in_chunks = (content_length != std::numeric_limits::max()); - m_transfer_encoding = (content_length == std::numeric_limits::max()); + m_sending_in_chunks = (content_length != (std::numeric_limits::max)()); + m_transfer_encoding = (content_length == (std::numeric_limits::max)()); m_remaining_to_write = content_length; - if (content_length == std::numeric_limits::max()) + if (content_length == (std::numeric_limits::max)()) { // Attempt to figure out the remaining length of the input stream m_remaining_to_write = m_response._get_impl()->_get_stream_length(); diff --git a/Release/src/http/listener/http_server_httpsys.h b/Release/src/http/listener/http_server_httpsys.h index d998619bbe..40c2a5e9ab 100644 --- a/Release/src/http/listener/http_server_httpsys.h +++ b/Release/src/http/listener/http_server_httpsys.h @@ -58,9 +58,9 @@ class http_overlapped : public OVERLAPPED ULONG_PTR numberOfBytesTransferred, PTP_IO io) { - CASABLANCA_UNREFERENCED_PARAMETER(io); - CASABLANCA_UNREFERENCED_PARAMETER(context); - CASABLANCA_UNREFERENCED_PARAMETER(instance); + (void)io; + (void)context; + (void)instance; http_overlapped* p_http_overlapped = (http_overlapped*)pOverlapped; p_http_overlapped->m_http_io_completion(result, (DWORD)numberOfBytesTransferred); diff --git a/Release/src/http/oauth/oauth1.cpp b/Release/src/http/oauth/oauth1.cpp index 506d9f1faa..b313cfcdb3 100644 --- a/Release/src/http/oauth/oauth1.cpp +++ b/Release/src/http/oauth/oauth1.cpp @@ -15,7 +15,7 @@ #include "cpprest/asyncrt_utils.h" -#if !defined(CPPREST_TARGET_XP) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA using namespace utility; using web::http::client::http_client; @@ -136,7 +136,7 @@ std::vector oauth1_config::_hmac_sha1(const utility::string_t& ke std::vector oauth1_config::_hmac_sha1(const utility::string_t& key, const utility::string_t& data) { - unsigned char digest[HMAC_MAX_MD_CBLOCK]; + unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len = 0; HMAC(EVP_sha1(), @@ -455,4 +455,4 @@ const oauth1_token& oauth1_config::token() const } // namespace http } // namespace web -#endif +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA diff --git a/Release/src/json/json.cpp b/Release/src/json/json.cpp index 7b61a179cc..079ccae473 100644 --- a/Release/src/json/json.cpp +++ b/Release/src/json/json.cpp @@ -38,7 +38,7 @@ web::json::value::value() { } -web::json::value::value(int32_t value) +web::json::value::value(int value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER , m_kind(value::Number) @@ -46,7 +46,7 @@ web::json::value::value(int32_t value) { } -web::json::value::value(uint32_t value) +web::json::value::value(unsigned value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER , m_kind(value::Number) @@ -54,7 +54,8 @@ web::json::value::value(uint32_t value) { } -web::json::value::value(int64_t value) + +web::json::value::value(long value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER , m_kind(value::Number) @@ -62,7 +63,23 @@ web::json::value::value(int64_t value) { } -web::json::value::value(uint64_t value) +web::json::value::value(unsigned long value) + : m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + , m_kind(value::Number) +#endif +{ +} + +web::json::value::value(long long value) + : m_value(utility::details::make_unique(value)) +#ifdef ENABLE_JSON_VALUE_VISUALIZER + , m_kind(value::Number) +#endif +{ +} + +web::json::value::value(unsigned long long value) : m_value(utility::details::make_unique(value)) #ifdef ENABLE_JSON_VALUE_VISUALIZER , m_kind(value::Number) @@ -162,13 +179,17 @@ web::json::value web::json::value::null() { return web::json::value(); } web::json::value web::json::value::number(double value) { return web::json::value(value); } -web::json::value web::json::value::number(int32_t value) { return web::json::value(value); } +web::json::value web::json::value::number(int value) { return web::json::value(value); } + +web::json::value web::json::value::number(unsigned value) { return web::json::value(value); } + +web::json::value web::json::value::number(long value) { return web::json::value(value); } -web::json::value web::json::value::number(uint32_t value) { return web::json::value(value); } +web::json::value web::json::value::number(unsigned long value) { return web::json::value(value); } -web::json::value web::json::value::number(int64_t value) { return web::json::value(value); } +web::json::value web::json::value::number(long long value) { return web::json::value(value); } -web::json::value web::json::value::number(uint64_t value) { return web::json::value(value); } +web::json::value web::json::value::number(unsigned long long value) { return web::json::value(value); } web::json::value web::json::value::boolean(bool value) { return web::json::value(value); } @@ -286,8 +307,9 @@ bool web::json::number::is_int32() const switch (m_type) { case signed_type: - return m_intval >= std::numeric_limits::min() && m_intval <= std::numeric_limits::max(); - case unsigned_type: return m_uintval <= std::numeric_limits::max(); + return m_intval >= (std::numeric_limits::min)() && + m_intval <= (std::numeric_limits::max)(); + case unsigned_type: return m_uintval <= (uint32_t)(std::numeric_limits::max)(); case double_type: default: return false; } @@ -297,8 +319,8 @@ bool web::json::number::is_uint32() const { switch (m_type) { - case signed_type: return m_intval >= 0 && m_intval <= std::numeric_limits::max(); - case unsigned_type: return m_uintval <= std::numeric_limits::max(); + case signed_type: return m_intval >= 0 && m_intval <= (std::numeric_limits::max)(); + case unsigned_type: return m_uintval <= (std::numeric_limits::max)(); case double_type: default: return false; } @@ -309,7 +331,7 @@ bool web::json::number::is_int64() const switch (m_type) { case signed_type: return true; - case unsigned_type: return m_uintval <= static_cast(std::numeric_limits::max()); + case unsigned_type: return m_uintval <= static_cast((std::numeric_limits::max)()); case double_type: default: return false; } diff --git a/Release/src/json/json_parsing.cpp b/Release/src/json/json_parsing.cpp index 2af0ed1629..cdef44287f 100644 --- a/Release/src/json/json_parsing.cpp +++ b/Release/src/json/json_parsing.cpp @@ -965,7 +965,7 @@ std::unique_ptr JSON_Parser::_ParseObject( if (tkn.m_error) goto error; // State 2: Looking for a colon. - if (tkn.kind != JSON_Parser::Token::TKN_Colon) goto done; + if (tkn.kind != JSON_Parser::Token::TKN_Colon) goto error; GetNextToken(tkn); if (tkn.m_error) goto error; @@ -1040,7 +1040,7 @@ std::unique_ptr JSON_Parser::_ParseArray( case JSON_Parser::Token::TKN_CloseBracket: GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); - return std::move(result); + return std::unique_ptr(result.release()); default: SetErrorCode(tkn, json_error::malformed_array_literal); return utility::details::make_unique(); @@ -1058,6 +1058,7 @@ template std::unique_ptr JSON_Parser::_ParseValue( typename JSON_Parser::Token& tkn) { + typedef std::unique_ptr Vptr; switch (tkn.kind) { case JSON_Parser::Token::TKN_OpenBrace: @@ -1070,15 +1071,15 @@ std::unique_ptr JSON_Parser::_ParseValue( } case JSON_Parser::Token::TKN_StringLiteral: { - auto value = utility::details::make_unique(std::move(tkn.string_val), + Vptr value = utility::details::make_unique(std::move(tkn.string_val), tkn.has_unescape_symbol); GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); + return value; } case JSON_Parser::Token::TKN_IntegerLiteral: { - std::unique_ptr value; + Vptr value; if (tkn.signed_number) value = utility::details::make_unique(tkn.int64_val); else @@ -1086,21 +1087,21 @@ std::unique_ptr JSON_Parser::_ParseValue( GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); + return value; } case JSON_Parser::Token::TKN_NumberLiteral: { - auto value = utility::details::make_unique(tkn.double_val); + Vptr value = utility::details::make_unique(tkn.double_val); GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); + return value; } case JSON_Parser::Token::TKN_BooleanLiteral: { - auto value = utility::details::make_unique(tkn.boolean_val); + Vptr value = utility::details::make_unique(tkn.boolean_val); GetNextToken(tkn); if (tkn.m_error) return utility::details::make_unique(); - return std::move(value); + return value; } case JSON_Parser::Token::TKN_NullLiteral: { @@ -1120,10 +1121,11 @@ std::unique_ptr JSON_Parser::_ParseValue( } // namespace json } // namespace web -static web::json::value _parse_stream(utility::istream_t& stream) +template +static web::json::value _parse_stream(std::basic_istream& stream) { - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_Parser::Token tkn; + web::json::details::JSON_StreamParser parser(stream); + typename web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) @@ -1136,7 +1138,7 @@ static web::json::value _parse_stream(utility::istream_t& stream) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } - else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); @@ -1144,10 +1146,11 @@ static web::json::value _parse_stream(utility::istream_t& stream) return value; } -static web::json::value _parse_stream(utility::istream_t& stream, std::error_code& error) +template +static web::json::value _parse_stream(std::basic_istream& stream, std::error_code& error) { - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_Parser::Token tkn; + web::json::details::JSON_StreamParser parser(stream); + typename web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) @@ -1157,7 +1160,7 @@ static web::json::value _parse_stream(utility::istream_t& stream, std::error_cod } auto returnObject = parser.ParseValue(tkn); - if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); } @@ -1166,11 +1169,11 @@ static web::json::value _parse_stream(utility::istream_t& stream, std::error_cod return returnObject; } -#ifdef _WIN32 -static web::json::value _parse_narrow_stream(std::istream& stream) +template +static web::json::value _parse_string(const std::basic_string& str) { - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_StreamParser::Token tkn; + web::json::details::JSON_StringParser parser(str); + typename web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) @@ -1183,7 +1186,7 @@ static web::json::value _parse_narrow_stream(std::istream& stream) { web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); } - else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { web::json::details::CreateException(tkn, _XPLATSTR("Left-over characters in stream after parsing a JSON value")); @@ -1191,10 +1194,11 @@ static web::json::value _parse_narrow_stream(std::istream& stream) return value; } -static web::json::value _parse_narrow_stream(std::istream& stream, std::error_code& error) +template +static web::json::value _parse_string(const std::basic_string& str, std::error_code& error) { - web::json::details::JSON_StreamParser parser(stream); - web::json::details::JSON_StreamParser::Token tkn; + web::json::details::JSON_StringParser parser(str); + typename web::json::details::JSON_Parser::Token tkn; parser.GetNextToken(tkn); if (tkn.m_error) @@ -1204,7 +1208,7 @@ static web::json::value _parse_narrow_stream(std::istream& stream, std::error_co } auto returnObject = parser.ParseValue(tkn); - if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) + if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) { returnObject = web::json::value(); web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); @@ -1213,53 +1217,12 @@ static web::json::value _parse_narrow_stream(std::istream& stream, std::error_co error = std::move(tkn.m_error); return returnObject; } -#endif -web::json::value web::json::value::parse(const utility::string_t& str) -{ - web::json::details::JSON_StringParser parser(str); - web::json::details::JSON_Parser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - - auto value = parser.ParseValue(tkn); - if (tkn.m_error) - { - web::json::details::CreateException(tkn, utility::conversions::to_string_t(tkn.m_error.message())); - } - else if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - web::json::details::CreateException(tkn, - _XPLATSTR("Left-over characters in stream after parsing a JSON value")); - } - return value; -} +web::json::value web::json::value::parse(const utility::string_t& str) { return _parse_string(str); } web::json::value web::json::value::parse(const utility::string_t& str, std::error_code& error) { - web::json::details::JSON_StringParser parser(str); - web::json::details::JSON_Parser::Token tkn; - - parser.GetNextToken(tkn); - if (tkn.m_error) - { - error = std::move(tkn.m_error); - return web::json::value(); - } - - auto returnObject = parser.ParseValue(tkn); - if (tkn.kind != web::json::details::JSON_Parser::Token::TKN_EOF) - { - returnObject = web::json::value(); - web::json::details::SetErrorCode(tkn, web::json::details::json_error::left_over_character_in_stream); - } - - error = std::move(tkn.m_error); - return returnObject; + return _parse_string(str, error); } web::json::value web::json::value::parse(utility::istream_t& stream) { return _parse_stream(stream); } @@ -1270,10 +1233,17 @@ web::json::value web::json::value::parse(utility::istream_t& stream, std::error_ } #ifdef _WIN32 -web::json::value web::json::value::parse(std::istream& stream) { return _parse_narrow_stream(stream); } +web::json::value web::json::value::parse(const std::string& str) { return _parse_string(str); } + +web::json::value web::json::value::parse(const std::string& str, std::error_code& error) +{ + return _parse_string(str, error); +} + +web::json::value web::json::value::parse(std::istream& stream) { return _parse_stream(stream); } web::json::value web::json::value::parse(std::istream& stream, std::error_code& error) { - return _parse_narrow_stream(stream, error); + return _parse_stream(stream, error); } #endif diff --git a/Release/src/json/json_serialization.cpp b/Release/src/json/json_serialization.cpp index 3aa0c50ceb..0191b34ff1 100644 --- a/Release/src/json/json_serialization.cpp +++ b/Release/src/json/json_serialization.cpp @@ -16,7 +16,9 @@ #include #ifndef _WIN32 +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS +#endif #include #endif diff --git a/Release/src/pch/stdafx.h b/Release/src/pch/stdafx.h index d999befe5b..5c398e14c4 100644 --- a/Release/src/pch/stdafx.h +++ b/Release/src/pch/stdafx.h @@ -20,32 +20,29 @@ #endif #ifdef _WIN32 -#define NOMINMAX -#ifdef CPPREST_TARGET_XP -#include -#ifndef _WIN32_WINNT -#define _WIN32_WINNT _WIN32_WINNT_WS03 // Windows XP with SP2 -#endif -#endif -#include // use the debug version of the CRT if _DEBUG is defined #ifdef _DEBUG #define _CRTDBG_MAP_ALLOC #include -#include -#endif +#endif // _DEBUG +#include #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#if CPPREST_TARGET_XP && _WIN32_WINNT != 0x0501 +#error CPPREST_TARGET_XP implies _WIN32_WINNT == 0x0501 +#endif // CPPREST_TARGET_XP && _WIN32_WINNT != 0x0501 + #include #include // Windows Header Files: -#if !defined(__cplusplus_winrt) +#ifndef __cplusplus_winrt #include +#endif // !__cplusplus_winrt -#endif // #if !defined(__cplusplus_winrt) -#else // LINUX or APPLE +#else // LINUX or APPLE #define __STDC_LIMIT_MACROS #include "pthread.h" #include @@ -84,6 +81,7 @@ #include #include #include +#include #include // json diff --git a/Release/src/streams/fileio_posix.cpp b/Release/src/streams/fileio_posix.cpp index 013d910d44..2404196423 100644 --- a/Release/src/streams/fileio_posix.cpp +++ b/Release/src/streams/fileio_posix.cpp @@ -375,7 +375,7 @@ size_t _fill_buffer_fsb(_file_info_impl* fInfo, _filestream_callback* callback, size_t byteCount = count * charSize; if (fInfo->m_buffer == nullptr) { - fInfo->m_bufsize = std::max(PageSize, byteCount); + fInfo->m_bufsize = (std::max)(PageSize, byteCount); fInfo->m_buffer = new char[static_cast(fInfo->m_bufsize)]; fInfo->m_bufoff = fInfo->m_rdpos; @@ -396,7 +396,7 @@ size_t _fill_buffer_fsb(_file_info_impl* fInfo, _filestream_callback* callback, if (bufrem < count) { - fInfo->m_bufsize = std::max(PageSize, byteCount); + fInfo->m_bufsize = (std::max)(PageSize, byteCount); // Then, we allocate a new buffer. @@ -456,7 +456,7 @@ size_t _getn_fsb(Concurrency::streams::details::_file_info* info, if (fInfo->m_buffer_reads) { auto cb = create_callback(fInfo, callback, [=](size_t read) { - auto copy = std::min(read, byteCount); + auto copy = (std::min)(read, byteCount); auto bufoff = fInfo->m_rdpos - fInfo->m_bufoff; memcpy(ptr, fInfo->m_buffer + bufoff * charSize, copy); fInfo->m_atend = copy < byteCount; @@ -467,7 +467,7 @@ size_t _getn_fsb(Concurrency::streams::details::_file_info* info, if (static_cast(read) > 0) { - auto copy = std::min(read, byteCount); + auto copy = (std::min)(read, byteCount); auto bufoff = fInfo->m_rdpos - fInfo->m_bufoff; memcpy(ptr, fInfo->m_buffer + bufoff * charSize, copy); fInfo->m_atend = copy < byteCount; diff --git a/Release/src/streams/fileio_win32.cpp b/Release/src/streams/fileio_win32.cpp index 86b5bd1ddc..057dd9b670 100644 --- a/Release/src/streams/fileio_win32.cpp +++ b/Release/src/streams/fileio_win32.cpp @@ -105,13 +105,13 @@ void CALLBACK IoCompletionCallback(PTP_CALLBACK_INSTANCE instance, ULONG_PTR numberOfBytesTransferred, PTP_IO io) { - CASABLANCA_UNREFERENCED_PARAMETER(io); - CASABLANCA_UNREFERENCED_PARAMETER(ctxt); - CASABLANCA_UNREFERENCED_PARAMETER(instance); + (void)io; + (void)ctxt; + (void)instance; EXTENDED_OVERLAPPED* pExtOverlapped = static_cast(pOverlapped); pExtOverlapped->func(result, static_cast(numberOfBytesTransferred), static_cast(pOverlapped)); - delete pOverlapped; + delete pExtOverlapped; } #endif @@ -399,10 +399,10 @@ size_t _write_file_async(_In_ streams::details::_file_info_impl* fInfo, size_t result = static_cast(-1); - if (wrResult == TRUE) + if (wrResult) { // If WriteFile returned true, it must be because the operation completed immediately. - // However, we didn't pass in an address for the number of bytes written, so + // However, we didn't pass in an address for the number of bytes written, so // we have to retrieve it using 'GetOverlappedResult,' which may, in turn, fail. DWORD written = 0; result = GetOverlappedResult(fInfo->m_handle, pOverlapped.get(), &written, FALSE) ? static_cast(written) @@ -496,7 +496,7 @@ size_t _read_file_async(_In_ streams::details::_file_info_impl* fInfo, size_t result = static_cast(-1); - if (wrResult == TRUE) + if (wrResult) { // If ReadFile returned true, it must be because the operation completed immediately. // However, we didn't pass in an address for the number of bytes written, so @@ -941,7 +941,7 @@ utility::size64_t __cdecl _get_size(_In_ concurrency::streams::details::_file_in LARGE_INTEGER size; - if (GetFileSizeEx(fInfo->m_handle, &size) == TRUE) + if (GetFileSizeEx(fInfo->m_handle, &size)) return utility::size64_t(size.QuadPart / char_size); else return 0; diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index 92781f67a8..3f2414af2c 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -734,7 +734,7 @@ std::map uri::split_query(const utility::s else if (equals_index == 0) { utility::string_t value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); - results[_XPLATSTR("")] = value; + results[utility::string_t {}] = value; } else { diff --git a/Release/src/utilities/asyncrt_utils.cpp b/Release/src/utilities/asyncrt_utils.cpp index 5263c8d5d5..cf747c666c 100644 --- a/Release/src/utilities/asyncrt_utils.cpp +++ b/Release/src/utilities/asyncrt_utils.cpp @@ -124,7 +124,7 @@ scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale() delete clocale; }; #else - *clocale = newlocale(LC_ALL, "C", nullptr); + *clocale = newlocale(LC_ALL_MASK, "C", nullptr); if (clocale == nullptr || *clocale == nullptr) { throw std::runtime_error("Unable to create 'C' locale."); @@ -177,8 +177,8 @@ scoped_c_thread_locale::~scoped_c_thread_locale() } } #elif (defined(ANDROID) || defined(__ANDROID__)) -scoped_c_thread_locale::scoped_c_thread_locale() {} -scoped_c_thread_locale::~scoped_c_thread_locale() {} +scoped_c_thread_locale::scoped_c_thread_locale() { } +scoped_c_thread_locale::~scoped_c_thread_locale() { } #else scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr) { @@ -620,7 +620,11 @@ utf16string __cdecl conversions::to_utf16string(const std::string& value) { retu static const int64_t NtToUnixOffsetSeconds = 11644473600; // diff between windows and unix epochs (seconds) -static bool year_is_leap_year(int year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); } +static bool year_is_leap_year_1601(int yearsSince1601) +{ + int decimalYear = yearsSince1601 + 1601; + return (decimalYear % 4 == 0 && (decimalYear % 100 != 0 || decimalYear % 400 == 0)); +} static const int SecondsInMinute = 60; static const int SecondsInHour = SecondsInMinute * 60; @@ -635,26 +639,18 @@ static const int SecondsInYear = SecondsInDay * DaysInYear; static const int SecondsIn4Years = SecondsInDay * DaysIn4Years; static const int64_t SecondsIn100Years = static_cast(SecondsInDay) * DaysIn100Years; static const int64_t SecondsIn400Years = static_cast(SecondsInDay) * DaysIn400Years; -static const int64_t SecondsFrom1900To2001 = INT64_C(3187296000); - -static const int64_t NtTo1900OffsetInterval = INT64_C(0x014F373BFDE04000); -static int count_leap_years(const int yearsSince1900) +static int count_leap_years_1601(int yearsSince1601) { - int tmpYears = yearsSince1900 + 299; // shift into 1601, the first 400 year cycle including 1900 - - int year400 = tmpYears / 400; - tmpYears -= year400 * 400; + int year400 = yearsSince1601 / 400; + yearsSince1601 -= year400 * 400; int result = year400 * 97; - int year100 = tmpYears / 100; - tmpYears -= year100 * 100; + int year100 = yearsSince1601 / 100; + yearsSince1601 -= year100 * 100; result += year100 * 24; - result += tmpYears / 4; - - // subtract off leap years from 1601 - result -= 72; + result += yearsSince1601 / 4; return result; } @@ -720,40 +716,63 @@ struct compute_year_result int secondsLeftThisYear; }; -static const int64_t secondsFrom1601To1900 = INT64_C(9435484800); - -static compute_year_result compute_year(int64_t secondsSince1900) +static compute_year_result compute_year_1601(int64_t secondsSince1601) { - int64_t secondsLeft = secondsSince1900 + secondsFrom1601To1900; // shift to start of this 400 year cycle + int year400 = static_cast(secondsSince1601 / SecondsIn400Years); + secondsSince1601 -= year400 * SecondsIn400Years; - int year400 = static_cast(secondsLeft / SecondsIn400Years); - secondsLeft -= year400 * SecondsIn400Years; + int year100 = static_cast(secondsSince1601 / SecondsIn100Years); + secondsSince1601 -= year100 * SecondsIn100Years; - int year100 = static_cast(secondsLeft / SecondsIn100Years); - secondsLeft -= year100 * SecondsIn100Years; - - int year4 = static_cast(secondsLeft / SecondsIn4Years); - int secondsInt = static_cast(secondsLeft - year4 * SecondsIn4Years); + int year4 = static_cast(secondsSince1601 / SecondsIn4Years); + int secondsInt = static_cast(secondsSince1601 - year4 * SecondsIn4Years); int year1 = secondsInt / SecondsInYear; - secondsInt -= year1 * SecondsInYear; + if (year1 == 4) + { + // this is the last day in a leap year + year1 = 3; + } - // shift back to 1900 base from 1601: - return {year400 * 400 + year100 * 100 + year4 * 4 + year1 - 299, secondsInt}; + secondsInt -= year1 * SecondsInYear; + return {year400 * 400 + year100 * 100 + year4 * 4 + year1, secondsInt}; } +// The constant below was calculated by running the following test program on a Windows machine: +// #include +// #include + +// int main() { +// SYSTEMTIME st; +// st.wYear = 9999; +// st.wMonth = 12; +// st.wDayOfWeek = 5; +// st.wDay = 31; +// st.wHour = 23; +// st.wMinute = 59; +// st.wSecond = 59; +// st.wMilliseconds = 999; + +// unsigned long long ft; +// if (SystemTimeToFileTime(&st, reinterpret_cast(&ft))) { +// printf("0x%016llX\n", ft); +// } else { +// puts("failed!"); +// } +// } + utility::string_t datetime::to_string(date_format format) const { - if (m_interval > INT64_C(2650467743990000000)) + const int64_t interval = static_cast(m_interval); + if (interval > INT64_C(0x24C85A5ED1C018F0)) { throw std::out_of_range("The requested year exceeds the year 9999."); } - const int64_t epochAdjusted = static_cast(m_interval) - NtTo1900OffsetInterval; - const int64_t secondsSince1900 = epochAdjusted / _secondTicks; // convert to seconds - const int fracSec = static_cast(epochAdjusted % _secondTicks); + const int64_t secondsSince1601 = interval / _secondTicks; // convert to seconds + const int fracSec = static_cast(interval % _secondTicks); - const auto yearData = compute_year(secondsSince1900); + const auto yearData = compute_year_1601(secondsSince1601); const int year = yearData.year; const int yearDay = yearData.secondsLeftThisYear / SecondsInDay; int leftover = yearData.secondsLeftThisYear % SecondsInDay; @@ -762,7 +781,7 @@ utility::string_t datetime::to_string(date_format format) const const int minute = leftover / SecondsInMinute; leftover = leftover % SecondsInMinute; - const auto& monthTable = year_is_leap_year(year) ? cumulative_days_to_month_leap : cumulative_days_to_month; + const auto& monthTable = year_is_leap_year_1601(year) ? cumulative_days_to_month_leap : cumulative_days_to_month; int month = 0; while (month < 11 && monthTable[month + 1] <= yearDay) { @@ -770,7 +789,7 @@ utility::string_t datetime::to_string(date_format format) const } const auto monthDay = yearDay - monthTable[month] + 1; - const auto weekday = static_cast((secondsSince1900 / SecondsInDay + 1) % 7); + const auto weekday = static_cast((secondsSince1601 / SecondsInDay + 1) % 7); char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0 // 1970-01-01T00:00:00.1234567Z\0 @@ -785,7 +804,7 @@ utility::string_t datetime::to_string(date_format format) const dayNames + 4 * weekday, monthDay, monthNames + 4 * month, - year + 1900, + year + 1601, hour, minute, leftover); @@ -795,7 +814,7 @@ utility::string_t datetime::to_string(date_format format) const dayNames + 4 * weekday, monthDay, monthNames + 4 * month, - year + 1900, + year + 1601, hour, minute, leftover); @@ -809,7 +828,7 @@ utility::string_t datetime::to_string(date_format format) const sprintf_s(outCursor, 20, "%04d-%02d-%02dT%02d:%02d:%02d", - year + 1900, + year + 1601, month + 1, monthDay, hour, @@ -817,7 +836,7 @@ utility::string_t datetime::to_string(date_format format) const leftover); #else // ^^^ _MSC_VER // !_MSC_VER vvv sprintf( - outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1900, month + 1, monthDay, hour, minute, leftover); + outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1601, month + 1, monthDay, hour, minute, leftover); #endif // _MSC_VER outCursor += 19; if (fracSec != 0) @@ -883,12 +902,12 @@ static const unsigned char max_days_in_month[12] = { 31 // Dec }; -static bool validate_day_month(int day, int month, int year) +static bool validate_day_month_1601(int day, int month, int year) { int maxDaysThisMonth; if (month == 1) { // Feb needs leap year testing - maxDaysThisMonth = 28 + year_is_leap_year(year); + maxDaysThisMonth = 28 + year_is_leap_year_1601(year); } else { @@ -898,9 +917,9 @@ static bool validate_day_month(int day, int month, int year) return day >= 1 && day <= maxDaysThisMonth; } -static int get_year_day(int month, int monthDay, int year) +static int get_year_day_1601(int month, int monthDay, int year) { - return cumulative_days_to_month[month] + monthDay + (year_is_leap_year(year) && month > 1) - 1; + return cumulative_days_to_month[month] + monthDay + (year_is_leap_year_1601(year) && month > 1) - 1; } template @@ -979,11 +998,21 @@ zone = "UT" / "GMT" ; Universal Time ; hours+min. (HHMM) */ - datetime __cdecl datetime::from_string(const utility::string_t& dateString, date_format format) { - datetime result; - int64_t secondsSince1900; + auto result = from_string_maximum_error(dateString, format); + if (result == datetime::maximum()) + { + return datetime(); + } + + return result; +} + +datetime __cdecl datetime::from_string_maximum_error(const utility::string_t& dateString, date_format format) +{ + datetime result = datetime::maximum(); + int64_t secondsSince1601; uint64_t fracSec = 0; auto str = dateString.c_str(); if (format == RFC_1123) @@ -1050,19 +1079,21 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 + (str[3] - _XPLATSTR('0')); - if (year < 1900) + if (year < 1601) { return result; } + year -= 1601; + // days in month validity check - if (!validate_day_month(monthDay, month, year)) + if (!validate_day_month_1601(monthDay, month, year)) { return result; } str += 5; // parsed year - const int yearDay = get_year_day(month, monthDay, year); + const int yearDay = get_year_day_1601(month, monthDay, year); if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]) || str[2] != _XPLATSTR(':') || !ascii_isdigit5(str[3]) || !ascii_isdigit(str[4])) @@ -1106,12 +1137,11 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date return result; } - year -= 1900; - int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay; + int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay; if (parsedWeekday != 7) { - const int actualWeekday = (daysSince1900 + 1) % 7; + const int actualWeekday = (daysSince1601 + 1) % 7; if (parsedWeekday != actualWeekday) { @@ -1119,8 +1149,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date } } - secondsSince1900 = - static_cast(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec; + secondsSince1601 = + static_cast(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec; if (!string_starts_with(str, "GMT") && !string_starts_with(str, "UT")) { @@ -1148,7 +1178,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date { tzHours = 8; } - else if ((tzCh == _XPLATSTR('+') || tzCh == _XPLATSTR('-')) && ascii_isdigit2(str[1]) && + else if ((str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-')) && ascii_isdigit2(str[1]) && ascii_isdigit(str[2]) && ascii_isdigit5(str[3]) && ascii_isdigit(str[4])) { tzCh = str[0]; @@ -1160,8 +1190,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date return result; } - secondsSince1900 = timezone_adjust(secondsSince1900, static_cast(tzCh), tzHours, tzMinutes); - if (secondsSince1900 < 0) + secondsSince1601 = timezone_adjust(secondsSince1601, static_cast(tzCh), tzHours, tzMinutes); + if (secondsSince1601 < 0) { return result; } @@ -1177,11 +1207,13 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 + (str[3] - _XPLATSTR('0')); - if (year < 1900) + if (year < 1601) { return result; } + year -= 1601; + str += 4; if (*str == _XPLATSTR('-')) { @@ -1215,24 +1247,22 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date } int monthDay = atoi2(str); - if (!validate_day_month(monthDay, month, year)) + if (!validate_day_month_1601(monthDay, month, year)) { return result; } - const int yearDay = get_year_day(month, monthDay, year); + const int yearDay = get_year_day_1601(month, monthDay, year); str += 2; - year -= 1900; - int daysSince1900 = year * DaysInYear + count_leap_years(year) + yearDay; + int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay; if (str[0] != _XPLATSTR('T') && str[0] != _XPLATSTR('t')) { // No time - secondsSince1900 = static_cast(daysSince1900) * SecondsInDay; + secondsSince1601 = static_cast(daysSince1601) * SecondsInDay; - result.m_interval = - static_cast(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval); + result.m_interval = static_cast(secondsSince1601 * _secondTicks + fracSec); return result; } @@ -1321,8 +1351,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date } } - secondsSince1900 = - static_cast(daysSince1900) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec; + secondsSince1601 = + static_cast(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec; if (str[0] == _XPLATSTR('Z') || str[0] == _XPLATSTR('z')) { @@ -1337,8 +1367,8 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date return result; } - secondsSince1900 = timezone_adjust(secondsSince1900, offsetDirection, atoi2(str + 1), atoi2(str + 4)); - if (secondsSince1900 < 0) + secondsSince1601 = timezone_adjust(secondsSince1601, offsetDirection, atoi2(str + 1), atoi2(str + 4)); + if (secondsSince1601 < 0) { return result; } @@ -1353,7 +1383,7 @@ datetime __cdecl datetime::from_string(const utility::string_t& dateString, date throw std::invalid_argument("unrecognized date format"); } - result.m_interval = static_cast(secondsSince1900 * _secondTicks + fracSec + NtTo1900OffsetInterval); + result.m_interval = static_cast(secondsSince1601 * _secondTicks + fracSec); return result; } diff --git a/Release/src/utilities/web_utilities.cpp b/Release/src/utilities/web_utilities.cpp index 9316f41f4f..ce00078b79 100644 --- a/Release/src/utilities/web_utilities.cpp +++ b/Release/src/utilities/web_utilities.cpp @@ -27,8 +27,9 @@ namespace web { namespace details { -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) -#if defined(__cplusplus_winrt) +#ifdef _WIN32 +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA +#ifdef __cplusplus_winrt // Helper function to zero out memory of an IBuffer. void winrt_secure_zero_buffer(Windows::Storage::Streams::IBuffer ^ buffer) @@ -88,7 +89,7 @@ plaintext_string winrt_encryption::decrypt() const return std::move(data); } -#else +#else // ^^^ __cplusplus_winrt ^^^ // vvv !__cplusplus_winrt vvv win32_encryption::win32_encryption(const std::wstring& data) : m_numCharacters(data.size()) { @@ -141,13 +142,14 @@ plaintext_string win32_encryption::decrypt() const return result; } -#endif -#endif +#endif // __cplusplus_winrt +#endif // _WIN32_WINNT >= _WIN32_WINNT_VISTA +#endif // _WIN32 void zero_memory_deleter::operator()(::utility::string_t* data) const { - CASABLANCA_UNREFERENCED_PARAMETER(data); -#if defined(_WIN32) + (void)data; +#ifdef _WIN32 SecureZeroMemory(&(*data)[0], data->size() * sizeof(::utility::string_t::value_type)); delete data; #endif diff --git a/Release/src/websockets/client/ws_client.cpp b/Release/src/websockets/client/ws_client.cpp index f768e968fb..48a40797e6 100644 --- a/Release/src/websockets/client/ws_client.cpp +++ b/Release/src/websockets/client/ws_client.cpp @@ -51,8 +51,7 @@ void websocket_client_task_impl::set_handler() }); m_callback_client->set_close_handler( - [=](websocket_close_status status, const utility::string_t& reason, const std::error_code& error_code) { - CASABLANCA_UNREFERENCED_PARAMETER(status); + [=](websocket_close_status, const utility::string_t& reason, const std::error_code& error_code) { close_pending_tasks_with_error(websocket_exception(error_code, reason)); }); } diff --git a/Release/src/websockets/client/ws_client_winrt.cpp b/Release/src/websockets/client/ws_client_winrt.cpp index 291ba8ce8a..6bb4351cee 100644 --- a/Release/src/websockets/client/ws_client_winrt.cpp +++ b/Release/src/websockets/client/ws_client_winrt.cpp @@ -404,7 +404,7 @@ class winrt_callback_client : public websocket_client_callback_impl, return close(websocket_close_status::normal, _XPLATSTR("Normal")); } - pplx::task close(websocket_close_status status, const utility::string_t& strreason = _XPLATSTR("")) + pplx::task close(websocket_close_status status, const utility::string_t& strreason = {}) { // Send a close frame to the server m_msg_websocket->Close(static_cast(status), Platform::StringReference(strreason.c_str())); diff --git a/Release/src/websockets/client/ws_client_wspp.cpp b/Release/src/websockets/client/ws_client_wspp.cpp index 326557fee5..d7c31c4095 100644 --- a/Release/src/websockets/client/ws_client_wspp.cpp +++ b/Release/src/websockets/client/ws_client_wspp.cpp @@ -292,7 +292,7 @@ class wspp_callback_client : public websocket_client_callback_impl, client.set_fail_handler([this](websocketpp::connection_hdl con_hdl) { _ASSERTE(m_state == CONNECTING); - shutdown_wspp_impl(con_hdl, true); + this->shutdown_wspp_impl(con_hdl, true); }); client.set_message_handler( @@ -325,9 +325,36 @@ class wspp_callback_client : public websocket_client_callback_impl, } }); + client.set_ping_handler([this](websocketpp::connection_hdl, const std::string& msg) { + if (m_external_message_handler) + { + _ASSERTE(m_state >= CONNECTED && m_state < CLOSED); + websocket_incoming_message incoming_msg; + + incoming_msg.m_msg_type = websocket_message_type::ping; + incoming_msg.m_body = concurrency::streams::container_buffer(msg); + + m_external_message_handler(incoming_msg); + } + return true; + }); + + client.set_pong_handler([this](websocketpp::connection_hdl, const std::string& msg) { + if (m_external_message_handler) + { + _ASSERTE(m_state >= CONNECTED && m_state < CLOSED); + websocket_incoming_message incoming_msg; + + incoming_msg.m_msg_type = websocket_message_type::pong; + incoming_msg.m_body = concurrency::streams::container_buffer(msg); + + m_external_message_handler(incoming_msg); + } + }); + client.set_close_handler([this](websocketpp::connection_hdl con_hdl) { _ASSERTE(m_state != CLOSED); - shutdown_wspp_impl(con_hdl, false); + this->shutdown_wspp_impl(con_hdl, false); }); // Set User Agent specified by the user. This needs to happen before any connection is created @@ -434,12 +461,14 @@ class wspp_callback_client : public websocket_client_callback_impl, { case websocket_message_type::text_message: case websocket_message_type::binary_message: + case websocket_message_type::ping: case websocket_message_type::pong: break; default: return pplx::task_from_exception(websocket_exception("Message Type not supported.")); } const auto length = msg.m_length; - if (length == 0 && msg.m_msg_type != websocket_message_type::pong) + if (length == 0 && msg.m_msg_type != websocket_message_type::ping && + msg.m_msg_type != websocket_message_type::pong) { return pplx::task_from_exception(websocket_exception("Cannot send empty message.")); } @@ -650,7 +679,7 @@ class wspp_callback_client : public websocket_client_callback_impl, client.stop_perpetual(); // Can't join thread directly since it is the current thread. - pplx::create_task([this, connecting, ec, closeCode, reason] { + pplx::create_task([] {}).then([this, connecting, ec, closeCode, reason]() mutable { { std::lock_guard lock(m_wspp_client_lock); if (m_thread.joinable()) @@ -659,9 +688,6 @@ class wspp_callback_client : public websocket_client_callback_impl, } } // unlock - // Delete client to make sure Websocketpp cleans up all Boost.Asio portions. - m_client.reset(); - if (connecting) { websocket_exception exc(ec, build_error_msg(ec, "set_fail_handler")); @@ -694,7 +720,18 @@ class wspp_callback_client : public websocket_client_callback_impl, case websocket_message_type::binary_message: client.send(this_client->m_con, sp_allocated.get(), length, websocketpp::frame::opcode::binary, ec); break; - case websocket_message_type::pong: client.pong(this_client->m_con, "", ec); break; + case websocket_message_type::ping: + { + std::string s(reinterpret_cast(sp_allocated.get()), length); + client.ping(this_client->m_con, s, ec); + break; + } + case websocket_message_type::pong: + { + std::string s(reinterpret_cast(sp_allocated.get()), length); + client.pong(this_client->m_con, s, ec); + break; + } default: // This case should have already been filtered above. std::abort(); @@ -760,8 +797,6 @@ class wspp_callback_client : public websocket_client_callback_impl, websocketpp::client m_client; }; - websocketpp::connection_hdl m_con; - pplx::task_completion_event m_connect_tce; pplx::task_completion_event m_close_tce; @@ -769,6 +804,7 @@ class wspp_callback_client : public websocket_client_callback_impl, std::mutex m_wspp_client_lock; State m_state; std::unique_ptr m_client; + websocketpp::connection_hdl m_con; // Queue to track pending sends outgoing_msg_queue m_out_queue; diff --git a/Release/tests/common/TestRunner/CMakeLists.txt b/Release/tests/common/TestRunner/CMakeLists.txt index 59b720d24c..48540d579a 100644 --- a/Release/tests/common/TestRunner/CMakeLists.txt +++ b/Release/tests/common/TestRunner/CMakeLists.txt @@ -8,6 +8,9 @@ endif() add_executable(test_runner test_runner.cpp test_module_loader.cpp) target_link_libraries(test_runner PRIVATE unittestpp ${CMAKE_DL_LIBS}) +if (WIN32) + target_sources(test_runner PRIVATE test_runner.manifest) +endif() if(BUILD_SHARED_LIBS AND NOT TEST_LIBRARY_TARGET_TYPE STREQUAL "OBJECT") elseif(APPLE) diff --git a/Release/tests/common/TestRunner/test_runner.cpp b/Release/tests/common/TestRunner/test_runner.cpp index 4d1a8d39f9..4913699d3d 100644 --- a/Release/tests/common/TestRunner/test_runner.cpp +++ b/Release/tests/common/TestRunner/test_runner.cpp @@ -238,7 +238,7 @@ static int parse_command_line(int argc, char** argv) } else { - UnitTest::GlobalSettings::Add(arg.substr(1), ""); + UnitTest::GlobalSettings::Add(arg.substr(1), std::string{}); } } else if (arg.find("/debug") == 0) @@ -262,9 +262,9 @@ static bool matched_properties(const UnitTest::TestProperties& test_props) // This starts with visual studio versions after VS 2012. #if defined(_MSC_VER) && (_MSC_VER >= 1800) #ifdef WINRT_TEST_RUNNER - UnitTest::GlobalSettings::Add("winrt", ""); + UnitTest::GlobalSettings::Add("winrt", std::string{}); #elif defined DESKTOP_TEST_RUNNER - UnitTest::GlobalSettings::Add("desktop", ""); + UnitTest::GlobalSettings::Add("desktop", std::string{}); #endif #endif @@ -557,8 +557,8 @@ int main(int argc, char* argv[]) breakOnError = true; } - // Determine if list or listProperties. - bool listOption = false, listPropertiesOption = false; + // The list_test_options() function determines if list or listProperties. + bool listOption = false; if (UnitTest::GlobalSettings::Has("list")) { listOption = true; @@ -566,7 +566,6 @@ int main(int argc, char* argv[]) if (UnitTest::GlobalSettings::Has("listproperties")) { listOption = true; - listPropertiesOption = true; } #ifdef _WIN32 if (UnitTest::GlobalSettings::Has("detectleaks")) diff --git a/Release/tests/common/TestRunner/test_runner.manifest b/Release/tests/common/TestRunner/test_runner.manifest new file mode 100644 index 0000000000..5621c557d6 --- /dev/null +++ b/Release/tests/common/TestRunner/test_runner.manifest @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Release/tests/common/UnitTestpp/src/Checks.h b/Release/tests/common/UnitTestpp/src/Checks.h index 4425fed100..f4494069b3 100644 --- a/Release/tests/common/UnitTestpp/src/Checks.h +++ b/Release/tests/common/UnitTestpp/src/Checks.h @@ -103,7 +103,7 @@ struct BuildFailureStringImpl std::string BuildString(const char*, const char*, const T1&, const T2&) { // Don't do anything since operator<< isn't supported. - return ""; + return std::string{}; } }; diff --git a/Release/tests/common/UnitTestpp/src/DeferredTestResult.cpp b/Release/tests/common/UnitTestpp/src/DeferredTestResult.cpp index 5800eeb6f1..703031f9fc 100644 --- a/Release/tests/common/UnitTestpp/src/DeferredTestResult.cpp +++ b/Release/tests/common/UnitTestpp/src/DeferredTestResult.cpp @@ -54,12 +54,12 @@ DeferredTestFailure::DeferredTestFailure(int lineNumber_, const char* failureStr } DeferredTestResult::DeferredTestResult() - : suiteName(""), testName(""), failureFile(""), timeElapsed(0.0f), failed(false) + : suiteName(), testName(), failureFile(), timeElapsed(0.0f), failed(false) { } -DeferredTestResult::DeferredTestResult(char const* suite, char const* test) - : suiteName(suite), testName(test), failureFile(""), timeElapsed(0.0f), failed(false) +DeferredTestResult::DeferredTestResult(char const* const suite, char const* const test) + : suiteName(suite), testName(test), failureFile(), timeElapsed(0.0f), failed(false) { } diff --git a/Release/tests/common/UnitTestpp/src/MemoryOutStream.cpp b/Release/tests/common/UnitTestpp/src/MemoryOutStream.cpp index c16845efb8..a0da2e4868 100644 --- a/Release/tests/common/UnitTestpp/src/MemoryOutStream.cpp +++ b/Release/tests/common/UnitTestpp/src/MemoryOutStream.cpp @@ -174,7 +174,7 @@ void MemoryOutStream::GrowBuffer(int const desiredCapacity) if (m_buffer) strcpy(buffer, m_buffer); else - strcpy(buffer, ""); + *buffer = '\0'; delete[] m_buffer; m_buffer = buffer; diff --git a/Release/tests/common/UnitTestpp/src/TestRunner.cpp b/Release/tests/common/UnitTestpp/src/TestRunner.cpp index 807a0e3b10..69551f3e2e 100644 --- a/Release/tests/common/UnitTestpp/src/TestRunner.cpp +++ b/Release/tests/common/UnitTestpp/src/TestRunner.cpp @@ -39,6 +39,7 @@ #include #include #else +#include #include #endif diff --git a/Release/tests/common/UnitTestpp/src/stdafx.h b/Release/tests/common/UnitTestpp/src/stdafx.h index 14b38623cb..99e91a15ee 100644 --- a/Release/tests/common/UnitTestpp/src/stdafx.h +++ b/Release/tests/common/UnitTestpp/src/stdafx.h @@ -49,6 +49,5 @@ #ifdef WIN32 #define WIN32_LEAN_AND_MEAN -#define NOMINMAX #include #endif \ No newline at end of file diff --git a/Release/tests/common/utilities/os_utilities.cpp b/Release/tests/common/utilities/os_utilities.cpp index de2a06d9fb..12aa4be6ef 100644 --- a/Release/tests/common/utilities/os_utilities.cpp +++ b/Release/tests/common/utilities/os_utilities.cpp @@ -12,7 +12,6 @@ #include "os_utilities.h" #ifdef WIN32 -#define NOMINMAX #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include diff --git a/Release/tests/functional/http/client/CMakeLists.txt b/Release/tests/functional/http/client/CMakeLists.txt index 45f0d9af02..3e1a93635a 100644 --- a/Release/tests/functional/http/client/CMakeLists.txt +++ b/Release/tests/functional/http/client/CMakeLists.txt @@ -16,6 +16,7 @@ set(SOURCES pipeline_stage_tests.cpp progress_handler_tests.cpp proxy_tests.cpp + redirect_tests.cpp request_helper_tests.cpp request_stream_tests.cpp request_uri_tests.cpp @@ -32,18 +33,7 @@ else() target_link_libraries(httpclient_test PRIVATE httptest_utilities) endif() -if(MSVC) - get_target_property(_srcs httpclient_test SOURCES) - - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/client-tests-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/client-tests-stdafx.pch") - endif() - - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fpclient-tests-stdafx.pch") - target_sources(httpclient_test PRIVATE stdafx.cpp) - target_compile_options(httpclient_test PRIVATE /Yustdafx.h /Fpclient-tests-stdafx.pch) -endif() +configure_pch(httpclient_test stdafx.h stdafx.cpp) if(NOT WIN32) cpprest_find_boost() diff --git a/Release/tests/functional/http/client/client_construction.cpp b/Release/tests/functional/http/client/client_construction.cpp index 1229b2cfd7..acad00eb61 100644 --- a/Release/tests/functional/http/client/client_construction.cpp +++ b/Release/tests/functional/http/client/client_construction.cpp @@ -173,7 +173,7 @@ SUITE(client_construction) VERIFY_ARE_EQUAL(baseclient2.base_uri(), m_uri); } -#if !defined(_WIN32) && !defined(__cplusplus_winrt) +#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) // Verify that the callback of sslcontext is called for HTTPS TEST_FIXTURE(uri_address, ssl_context_callback_https) @@ -183,7 +183,7 @@ SUITE(client_construction) config.set_ssl_context_callback([&called](boost::asio::ssl::context& ctx) { called = true; }); - http_client client("https://www.google.com/", config); + http_client client(U("https://www.google.com/"), config); try { @@ -204,7 +204,7 @@ SUITE(client_construction) config.set_ssl_context_callback([&called](boost::asio::ssl::context& ctx) { called = true; }); - http_client client("http://www.google.com/", config); + http_client client(U("http://www.google.com/"), config); try { diff --git a/Release/tests/functional/http/client/compression_tests.cpp b/Release/tests/functional/http/client/compression_tests.cpp index 128006431b..46ac3f42ac 100644 --- a/Release/tests/functional/http/client/compression_tests.cpp +++ b/Release/tests/functional/http/client/compression_tests.cpp @@ -73,7 +73,7 @@ SUITE(compression_tests) << " / " << _size; throw std::runtime_error(std::move(ss.str())); } - bytes = std::min(input_size, output_size); + bytes = (std::min)(input_size, output_size); if (bytes) { memcpy(output, input, bytes); @@ -128,7 +128,7 @@ SUITE(compression_tests) << " / " << _size; throw std::runtime_error(std::move(ss.str())); } - bytes = std::min(input_size, output_size); + bytes = (std::min)(input_size, output_size); if (bytes) { memcpy(output, input, bytes); @@ -206,7 +206,7 @@ SUITE(compression_tests) std::vector cmp_buffer(buffer_size); size_t cmpsize = buffer_size; size_t csize = 0; - operation_result r = {0}; + operation_result r = {}; operation_hint hint = operation_hint::has_more; for (i = 0; i < buffer_size || csize == cmpsize || !r.done; i += r.input_bytes_processed) { @@ -218,18 +218,18 @@ SUITE(compression_tests) if (csize == cmpsize) { // extend the output buffer if there may be more compressed bytes to retrieve - cmpsize += std::min(chunk_size, (size_t)200); + cmpsize += (std::min)(chunk_size, (size_t)200); cmp_buffer.resize(cmpsize); } r = compressor ->compress(input_buffer.data() + i, - std::min(chunk_size, buffer_size - i), + (std::min)(chunk_size, buffer_size - i), cmp_buffer.data() + csize, - std::min(chunk_size, cmpsize - csize), + (std::min)(chunk_size, cmpsize - csize), hint) .get(); - VERIFY_IS_TRUE(r.input_bytes_processed == std::min(chunk_size, buffer_size - i) || - r.output_bytes_produced == std::min(chunk_size, cmpsize - csize)); + VERIFY_IS_TRUE(r.input_bytes_processed == (std::min)(chunk_size, buffer_size - i) || + r.output_bytes_produced == (std::min)(chunk_size, cmpsize - csize)); VERIFY_IS_TRUE(hint == operation_hint::is_last || !r.done); chunk_sizes.push_back(r.output_bytes_produced); csize += r.output_bytes_produced; @@ -262,7 +262,7 @@ SUITE(compression_tests) ->decompress(cmp_buffer.data() + nn, *it, dcmp_buffer.data() + dsize, - std::min(chunk_size, buffer_size - dsize), + (std::min)(chunk_size, buffer_size - dsize), hint) .get(); nn += *it; @@ -281,14 +281,14 @@ SUITE(compression_tests) memset(dcmp_buffer.data(), 0, dcmp_buffer.size()); do { - size_t n = std::min(chunk_size, csize - nn); + size_t n = (std::min)(chunk_size, csize - nn); do { r = decompressor ->decompress(cmp_buffer.data() + nn, n, dcmp_buffer.data() + dsize, - std::min(chunk_size, buffer_size - dsize), + (std::min)(chunk_size, buffer_size - dsize), operation_hint::has_more) .get(); dsize += r.output_bytes_produced; @@ -361,9 +361,9 @@ SUITE(compression_tests) if (!cfactory) { auto size = tuples[i][0]; - compress_and_decompress(utility::details::make_unique(tuples[i][0]), - utility::details::make_unique(tuples[i][0]), - tuples[i][0], + compress_and_decompress(utility::details::make_unique(size), + utility::details::make_unique(size), + size, tuples[i][1], !!j); } @@ -376,19 +376,31 @@ SUITE(compression_tests) } } - TEST_FIXTURE(uri_address, compress_and_decompress) + TEST_FIXTURE(uri_address, compress_and_decompress_fake) { compress_test(nullptr, nullptr); // FAKE + } + + TEST_FIXTURE(uri_address, compress_and_decompress_gzip) + { if (builtin::algorithm::supported(builtin::algorithm::GZIP)) { compress_test(builtin::get_compress_factory(builtin::algorithm::GZIP), builtin::get_decompress_factory(builtin::algorithm::GZIP)); } + } + + TEST_FIXTURE(uri_address, compress_and_decompress_deflate) + { if (builtin::algorithm::supported(builtin::algorithm::DEFLATE)) { compress_test(builtin::get_compress_factory(builtin::algorithm::DEFLATE), builtin::get_decompress_factory(builtin::algorithm::DEFLATE)); } + } + + TEST_FIXTURE(uri_address, compress_and_decompress_brotli) + { if (builtin::algorithm::supported(builtin::algorithm::BROTLI)) { compress_test(builtin::get_compress_factory(builtin::algorithm::BROTLI), @@ -770,7 +782,7 @@ SUITE(compression_tests) } #endif // _WIN32 - auto extra_size = [](size_t bufsz) -> size_t { return std::max(static_cast(128), bufsz / 1000); }; + auto extra_size = [](size_t bufsz) -> size_t { return (std::max)(static_cast(128), bufsz / 1000); }; // Test decompression both explicitly through the test server and implicitly through the listener; // this is the top-level loop in order to avoid thrashing the listeners more than necessary diff --git a/Release/tests/functional/http/client/connections_and_errors.cpp b/Release/tests/functional/http/client/connections_and_errors.cpp index aea69b8c08..847755d80a 100644 --- a/Release/tests/functional/http/client/connections_and_errors.cpp +++ b/Release/tests/functional/http/client/connections_and_errors.cpp @@ -341,7 +341,9 @@ SUITE(connections_and_errors) test_http_server::scoped_server server(m_uri); pplx::cancellation_token_source source; + const auto r = server.server()->next_request(); responseTask = c.request(methods::GET, U("/"), source.get_token()); + r.wait(); source.cancel(); } diff --git a/Release/tests/functional/http/client/oauth2_tests.cpp b/Release/tests/functional/http/client/oauth2_tests.cpp index e1f5408588..08bb12a652 100644 --- a/Release/tests/functional/http/client/oauth2_tests.cpp +++ b/Release/tests/functional/http/client/oauth2_tests.cpp @@ -291,6 +291,74 @@ SUITE(oauth2_tests) VERIFY_ARE_EQUAL(U("done"), m_oauth2_config.token().access_token()); } + TEST_FIXTURE(oauth2_test_setup, oauth2_token_from_client_credentials) + { + VERIFY_IS_FALSE(m_oauth2_config.is_enabled()); + + m_oauth2_config.set_user_agent(U("test_user_agent")); + + // Fetch using HTTP Basic authentication. + { + m_scoped.server()->next_request().then([](test_request* request) { + VERIFY_ARE_EQUAL(request->m_method, methods::POST); + + VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request)); + + VERIFY_ARE_EQUAL( + U("Basic MTIzQUJDOjQ1NkRFRg=="), + request->m_headers[header_names::authorization]); + + VERIFY_ARE_EQUAL( + to_body_data(U("grant_type=client_credentials")), + request->m_body); + + VERIFY_ARE_EQUAL( + U("test_user_agent"), + get_request_user_agent(request)); + + std::map headers; + headers[header_names::content_type] = mime_types::application_json; + request->reply( + status_codes::OK, U(""), headers, "{\"access_token\":\"xyzzy123\",\"token_type\":\"bearer\"}"); + }); + + m_oauth2_config.token_from_client_credentials().wait(); + VERIFY_ARE_EQUAL(U("xyzzy123"), m_oauth2_config.token().access_token()); + VERIFY_IS_TRUE(m_oauth2_config.is_enabled()); + } + + // Fetch using client key & secret in request body (x-www-form-urlencoded). + { + m_scoped.server()->next_request().then([](test_request* request) { + VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request)); + + VERIFY_ARE_EQUAL(U(""), request->m_headers[header_names::authorization]); + + VERIFY_ARE_EQUAL( + to_body_data(U("grant_type=client_credentials&client_id=123ABC&client_secret=456DEF")), + request->m_body); + + VERIFY_ARE_EQUAL(U("test_user_agent"), get_request_user_agent(request)); + + std::map headers; + headers[header_names::content_type] = mime_types::application_json; + request->reply( + status_codes::OK, U(""), headers, "{\"access_token\":\"xyzzy123\",\"token_type\":\"bearer\"}"); + }); + + m_oauth2_config.set_token(oauth2_token()); // Clear token. + VERIFY_IS_FALSE(m_oauth2_config.is_enabled()); + + m_oauth2_config.set_http_basic_auth(false); + m_oauth2_config.token_from_client_credentials().wait(); + + VERIFY_ARE_EQUAL( + U("xyzzy123"), + m_oauth2_config.token().access_token()); + VERIFY_IS_TRUE(m_oauth2_config.is_enabled()); + } + } + TEST_FIXTURE(oauth2_test_setup, oauth2_bearer_token) { m_oauth2_config.set_token(oauth2_token(U("12345678"))); diff --git a/Release/tests/functional/http/client/outside_tests.cpp b/Release/tests/functional/http/client/outside_tests.cpp index 41709bc3a1..439772c72d 100644 --- a/Release/tests/functional/http/client/outside_tests.cpp +++ b/Release/tests/functional/http/client/outside_tests.cpp @@ -50,14 +50,12 @@ SUITE(outside_tests) // CNN's main page doesn't use chunked transfer encoding. http_response response = client.request(methods::GET).get(); - auto code = response.status_code(); - VERIFY_IS_TRUE(code == status_codes::OK || code == status_codes::MovedPermanently); + VERIFY_ARE_EQUAL(status_codes::OK, response.status_code()); response.content_ready().wait(); // CNN's other pages do use chunked transfer encoding. response = client.request(methods::GET, U("us")).get(); - code = response.status_code(); - VERIFY_IS_TRUE(code == status_codes::OK || code == status_codes::MovedPermanently); + VERIFY_ARE_EQUAL(status_codes::OK, response.status_code()); response.content_ready().wait(); }); } @@ -228,7 +226,7 @@ SUITE(outside_tests) TEST(server_hostname_mismatch) { test_failed_ssl_cert(U("https://wrong.host.badssl.com/")); } -#if !defined(__cplusplus_winrt) +#if !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) TEST(server_hostname_host_override) { handle_timeout([] { @@ -248,12 +246,9 @@ SUITE(outside_tests) http_request req(methods::GET); req.headers().add(U("Host"), U("en.wikipedia.org")); auto response = client.request(req).get(); - // WinHTTP will transparently follow the HTTP 301 upgrade request redirect, - // ASIO does not and will return the 301 directly. - const auto statusCode = response.status_code(); - CHECK(statusCode == status_codes::OK || statusCode == status_codes::MovedPermanently); + VERIFY_ARE_EQUAL(status_codes::OK, response.status_code()); } -#endif // !defined(__cplusplus_winrt) +#endif // !defined(__cplusplus_winrt) && !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) TEST(server_cert_expired) { test_failed_ssl_cert(U("https://expired.badssl.com/")); } @@ -290,77 +285,6 @@ SUITE(outside_tests) }); } #endif // !defined(__cplusplus_winrt) - - TEST_FIXTURE(uri_address, outside_ssl_json) - { - // Create URI for: - // https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=UUF1hMUVwlrvlVMjUGOZExgg&key=AIzaSyAviHxf_y0SzNoAq3iKqvWVE4KQ0yylsnk - uri_builder playlistUri(U("https://www.googleapis.com/youtube/v3/playlistItems?")); - playlistUri.append_query(U("part"), U("snippet")); - playlistUri.append_query(U("playlistId"), U("UUF1hMUVwlrvlVMjUGOZExgg")); - playlistUri.append_query(U("key"), U("AIzaSyAviHxf_y0SzNoAq3iKqvWVE4KQ0yylsnk")); - - // Send request - web::http::client::http_client playlistClient(playlistUri.to_uri()); - - handle_timeout([&] { - // Retry up to 4 times. - for (int i = 0; i < 4; ++i) - { - try - { - playlistClient.request(methods::GET) - .then([=](http_response playlistResponse) -> pplx::task { - return playlistResponse.extract_json(); - }) - .then([=](json::value v) { - int count = 0; - auto& obj = v.as_object(); - - VERIFY_ARE_NOT_EQUAL(obj.find(U("pageInfo")), obj.end()); - VERIFY_ARE_NOT_EQUAL(obj.find(U("items")), obj.end()); - - auto& items = obj[U("items")]; - - for (auto iter = items.as_array().cbegin(); iter != items.as_array().cend(); ++iter) - { - const auto& item = *iter; - auto iSnippet = item.as_object().find(U("snippet")); - if (iSnippet == item.as_object().end()) - { - throw std::runtime_error("snippet key not found"); - } - auto iTitle = iSnippet->second.as_object().find(U("title")); - if (iTitle == iSnippet->second.as_object().end()) - { - throw std::runtime_error("title key not found"); - } - auto name = iTitle->second.serialize(); - count++; - } - VERIFY_ARE_EQUAL(3, count); // Update this accordingly, if the number of items changes - }) - .wait(); - break; - } - catch (web::http::http_exception const& e) - { -#if defined(_MSC_VER) && !defined(__cplusplus_winrt) - if (e.error_code().value() != API_QUERY_DATA_AVAILABLE || i == 3) - { - // If we didn't get a "connection broken" error (or we are on the last retry), rethrow it - throw; - } -#else - CASABLANCA_UNREFERENCED_PARAMETER(e); - throw; -#endif - os_utilities::sleep(1000); - } - } - }); - } - } // SUITE(outside_tests) } // namespace client diff --git a/Release/tests/functional/http/client/redirect_tests.cpp b/Release/tests/functional/http/client/redirect_tests.cpp new file mode 100644 index 0000000000..a9d4179450 --- /dev/null +++ b/Release/tests/functional/http/client/redirect_tests.cpp @@ -0,0 +1,342 @@ +/*** + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Tests cases for multiple requests and responses from an http_client. + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ + +#include "stdafx.h" +#ifdef _WIN32 +#include +#include +#endif // _WIN32 + +using namespace web::http; +using namespace web::http::client; + +using namespace tests::functional::http::utilities; + +#if defined(_WIN32) && !defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) +#define USING_WINHTTP 1 +#else +#define USING_WINHTTP 0 +#endif + +namespace tests +{ +namespace functional +{ +namespace http +{ +namespace client +{ +pplx::task next_reply_assert( + test_http_server* p_server, + const method& method, + const utility::string_t& path, + status_code code = status_codes::OK, + const utility::string_t& location = U("")) +{ + return p_server->next_request().then([=](test_request* p_request) { + http_asserts::assert_test_request_equals(p_request, method, path); + size_t content_length; + VERIFY_ARE_EQUAL(methods::POST == method, + p_request->match_header(header_names::content_length, content_length)); + + std::map headers; + if (!location.empty()) + { + headers[header_names::location] = location; + } + + // web::http::details::get_default_reason_phrase is internal :-/ + p_request->reply(code, {}, headers); + }); +} + +pplx::task next_reply_assert( + test_http_server* p_server, + const utility::string_t& path, + status_code code = status_codes::OK, + const utility::string_t& location = U("")) +{ + return next_reply_assert(p_server, methods::GET, path, code, location); +} + +SUITE(redirect_tests) +{ + TEST_FIXTURE(uri_address, follows_multiple_redirects_by_default) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::MovedPermanently, U("/moved-here"))); + replies.push_back(next_reply_assert(p_server, U("/moved-here"), status_codes::TemporaryRedirect, U("/moved-there"))); + replies.push_back(next_reply_assert(p_server, U("/moved-there"), status_codes::Found, U("/found-elsewhere"))); + replies.push_back(next_reply_assert(p_server, U("/found-elsewhere"))); + + http_client_config config; + http_client client(m_uri, config); + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::OK) + ); + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST_FIXTURE(uri_address, follows_retrieval_redirect) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, methods::POST, U("/"), status_codes::SeeOther, U("/see-here"))); + replies.push_back(next_reply_assert(p_server, methods::GET, U("/see-here"))); + + http_client_config config; + http_client client(m_uri, config); + + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::POST, U(""), U("body")).get(), status_codes::OK); + ); + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST_FIXTURE(uri_address, obeys_max_redirects) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::MovedPermanently, U("/moved-here"))); + replies.push_back(next_reply_assert(p_server, U("/moved-here"), status_codes::TemporaryRedirect, U("/moved-there"))); + replies.push_back(next_reply_assert(p_server, U("/moved-there"), status_codes::Found, U("/found-elsewhere"))); + + http_client_config config; + config.set_max_redirects(2); + http_client client(m_uri, config); + + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::Found) + ); + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST_FIXTURE(uri_address, can_disable_redirects) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::MovedPermanently, U("/moved-here"))); + + http_client_config config; + config.set_max_redirects(0); + http_client client(m_uri, config); + + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::MovedPermanently) + ); + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST(does_not_follow_https_to_http_by_default) + { + handle_timeout([] { + http_client_config config; + http_client client(U("https://http.badssl.com/"), config); + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::MovedPermanently) + ); + }); + } + + TEST(can_follow_https_to_http) + { + handle_timeout([] { + http_client_config config; + config.set_https_to_http_redirects(true); + http_client client(U("https://http.badssl.com/"), config); + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::OK) + ); + }); + } + + TEST_FIXTURE(uri_address, follows_permanent_redirect) + { +#if USING_WINHTTP + // note that 308 Permanent Redirect is only supported by WinHTTP from Windows 10 + if (!IsWindows10OrGreater()) { + return; + } +#endif // USING_WINHTTP + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::PermanentRedirect, U("/moved-here"))); + replies.push_back(next_reply_assert(p_server, U("/moved-here"))); + + http_client_config config; + http_client client(m_uri, config); + + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::OK) + ); + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST_FIXTURE(uri_address, may_throw_if_no_location) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::MovedPermanently)); + + http_client_config config; + http_client client(m_uri, config); + + // implementation-specific behaviour +#if USING_WINHTTP + VERIFY_THROWS( + client.request(methods::GET).get(), + http_exception + ); +#else + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::MovedPermanently) + ); +#endif + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST_FIXTURE(uri_address, should_not_follow_cyclic_redirect) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::TemporaryRedirect, U("/briefly-here"))); + replies.push_back(next_reply_assert(p_server, U("/briefly-here"), status_codes::MovedPermanently, U("/"))); +#if USING_WINHTTP + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::NotFound)); +#endif + + http_client_config config; + http_client client(m_uri, config); + + // implementation-specific behaviour +#if USING_WINHTTP + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::NotFound) + ); +#else // ^^^ USING_WINHTTP / !USING_WINHTTP vvv + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::MovedPermanently) + ); +#endif // USING_WINHTTP + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST_FIXTURE(uri_address, may_follow_unchanged_redirect) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, methods::POST, U("/"), status_codes::TemporaryRedirect, U("/retry-here"))); +#if USING_WINHTTP + replies.push_back(next_reply_assert(p_server, methods::POST, U("/retry-here"))); +#endif + + http_client_config config; + http_client client(m_uri, config); + + // implementation-specific behaviour +#if USING_WINHTTP + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::POST, U(""), U("body")).get(), status_codes::OK) + ); +#else // ^^^ USING_WINHTTP / !USING_WINHTTP vvv + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::POST, U(""), U("body")).get(), status_codes::TemporaryRedirect) + ); +#endif // USING_WINHTTP + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + + TEST_FIXTURE(uri_address, may_not_follow_manual_redirect) + { + test_http_server::scoped_server scoped(m_uri); + auto p_server = scoped.server(); + + std::vector> replies; + replies.push_back(next_reply_assert(p_server, U("/"), status_codes::MultipleChoices, U("/prefer-here"))); +#if USING_WINHTTP + replies.push_back(next_reply_assert(p_server, U("/prefer-here"))); +#endif + + http_client_config config; + http_client client(m_uri, config); + + // implementation-specific behaviour +#if USING_WINHTTP + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::OK) + ); +#else // ^^^ USING_WINHTTP / !USING_WINHTTP vvv + VERIFY_NO_THROWS( + http_asserts::assert_response_equals(client.request(methods::GET).get(), status_codes::MultipleChoices) + ); +#endif // USING_WINHTTP + p_server->close(); + for (auto& reply : replies) + { + VERIFY_NO_THROWS(reply.get()); + } + } + +} // SUITE(redirect_tests) + +} // namespace client +} // namespace http +} // namespace functional +} // namespace tests diff --git a/Release/tests/functional/http/client/request_uri_tests.cpp b/Release/tests/functional/http/client/request_uri_tests.cpp index c856b600e3..5d61556991 100644 --- a/Release/tests/functional/http/client/request_uri_tests.cpp +++ b/Release/tests/functional/http/client/request_uri_tests.cpp @@ -88,7 +88,7 @@ SUITE(request_uri_tests) // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/heheh?key1=value2#fragment"); -#ifdef __cplusplus_winrt +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) expected_value = percent_encode_pound(expected_value); #endif @@ -137,7 +137,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1?key1=value1&key2=value2#frag"); -#ifdef __cplusplus_winrt +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); @@ -157,7 +157,7 @@ SUITE(request_uri_tests) p_server->next_request().then([&](test_request* p_request) { // WinRT implementation percent encodes the '#'. utility::string_t expected_value = U("/path1/path2?key2=value2#fragmentfg2"); -#ifdef __cplusplus_winrt +#if defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL) expected_value = percent_encode_pound(expected_value); #endif http_asserts::assert_test_request_equals(p_request, methods::GET, expected_value); diff --git a/Release/tests/functional/http/listener/CMakeLists.txt b/Release/tests/functional/http/listener/CMakeLists.txt index aa4245fed8..58cf86a6fd 100644 --- a/Release/tests/functional/http/listener/CMakeLists.txt +++ b/Release/tests/functional/http/listener/CMakeLists.txt @@ -22,14 +22,5 @@ if(NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) target_link_libraries(httplistener_test PRIVATE httptest_utilities) endif() - if(MSVC) - get_target_property(_srcs httplistener_test SOURCES) - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/listener-tests-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/listener-tests-stdafx.pch") - endif() - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fplistener-tests-stdafx.pch") - target_sources(httplistener_test PRIVATE stdafx.cpp) - target_compile_options(httplistener_test PRIVATE /Yustdafx.h /Fplistener-tests-stdafx.pch) - endif() + configure_pch(httplistener_test stdafx.h stdafx.cpp) endif() diff --git a/Release/tests/functional/http/listener/header_tests.cpp b/Release/tests/functional/http/listener/header_tests.cpp index a3cff2e2e1..87cf6783fb 100644 --- a/Release/tests/functional/http/listener/header_tests.cpp +++ b/Release/tests/functional/http/listener/header_tests.cpp @@ -112,6 +112,31 @@ SUITE(header_tests) listener.close().wait(); } + TEST_FIXTURE(uri_address, request_known_headers) + { + http_listener listener(m_uri); + listener.open().wait(); + test_http_client::scoped_client client(m_uri); + test_http_client* p_client = client.client(); + const utility::string_t mtd = methods::GET; + std::map headers; + + // "Date" was being incorrectly mapped to "Data" + // see https://github.com/microsoft/cpprestsdk/issues/1208 + headers[U("Date")] = U("Mon, 29 Jul 2019 12:32:57 GMT"); + listener.support([&](http_request request) { + http_asserts::assert_request_equals(request, mtd, U("/"), headers); + request.reply(status_codes::OK).wait(); + }); + VERIFY_ARE_EQUAL(0, p_client->request(mtd, U(""), headers)); + p_client->next_response() + .then([](test_response* p_response) { + http_asserts::assert_test_response_equals(p_response, status_codes::OK); + }) + .wait(); + listener.close().wait(); + } + TEST_FIXTURE(uri_address, response_headers) { http_listener listener(m_uri); diff --git a/Release/tests/functional/http/listener/listener_construction_tests.cpp b/Release/tests/functional/http/listener/listener_construction_tests.cpp index 1e93ef56ae..c6d95876ce 100644 --- a/Release/tests/functional/http/listener/listener_construction_tests.cpp +++ b/Release/tests/functional/http/listener/listener_construction_tests.cpp @@ -549,9 +549,13 @@ XzJTD4slrGSJrcpLt/g/Jqqdjg== listener.open().wait(); client::http_client_config client_config; +#if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) client_config.set_ssl_context_callback( [&](boost::asio::ssl::context& ctx) { ctx.add_certificate_authority(cert); }); - +#else + // in this build configuration, with WinHTTP-based http_client, this test will fail unless the self-signed + // cert is added to the Windows certificate store (or certificate validation is disabled in client_config) +#endif client::http_client client(m_secure_uri, client_config); http_request msg(methods::GET); msg.set_request_uri(U("/")); diff --git a/Release/tests/functional/http/utilities/stdafx.h b/Release/tests/functional/http/utilities/stdafx.h index 9ce41e4b72..23e69e4535 100644 --- a/Release/tests/functional/http/utilities/stdafx.h +++ b/Release/tests/functional/http/utilities/stdafx.h @@ -13,7 +13,6 @@ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN -#define NOMINMAX #include #include diff --git a/Release/tests/functional/http/utilities/test_http_server.cpp b/Release/tests/functional/http/utilities/test_http_server.cpp index e3a96570ab..3abb6915b6 100644 --- a/Release/tests/functional/http/utilities/test_http_server.cpp +++ b/Release/tests/functional/http/utilities/test_http_server.cpp @@ -97,62 +97,53 @@ static utility::string_t parse_verb(const HTTP_REQUEST* p_http_request) } /// -/// String values for all HTTP Server API known headers. +/// String values for all HTTP Server API HTTP_REQUEST_HEADERS known headers. /// NOTE: the order here is important it is from the _HTTP_HEADER_ID enum. /// -static utility::string_t HttpServerAPIKnownHeaders[] = {U("Cache-Control"), - U("Connection"), - U("Data"), - U("Keep-Alive"), - U("Pragma"), - U("Trailer"), - U("Transfer-Encoding"), - U("Upgrade"), - U("Via"), - U("Warning"), - U("Allow"), - U("Content-Length"), - U("Content-Type"), - U("Content-Encoding"), - U("Content-Language"), - U("Content-Location"), - U("Content-Md5"), - U("Content-Range"), - U("Expires"), - U("Last-Modified"), - U("Accept"), - U("Accept-Charset"), - U("Accept-Encoding"), - U("Accept-Language"), - U("Authorization"), - U("Cookie"), - U("Expect"), - U("From"), - U("Host"), - U("If-Match"), - U("If-Modified-Since"), - U("If-None-Match"), - U("If-Range"), - U("If-Unmodified-Since"), - U("Max-Forwards"), - U("Proxy-Authorization"), - U("Referer"), - U("Range"), - U("TE"), - U("Translate"), - U("User-Agent"), - U("Request-Maximum"), - U("Accept-Ranges"), - U("Age"), - U("Etag"), - U("Location"), - U("Proxy-Authenticate"), - U("Retry-After"), - U("Server"), - U("Set-Cookie"), - U("Vary"), - U("Www-Authenticate"), - U("Response-Maximum")}; +static utility::string_t HttpServerAPIRequestKnownHeaders[] = +{ + U("Cache-Control"), + U("Connection"), + U("Date"), + U("Keep-Alive"), + U("Pragma"), + U("Trailer"), + U("Transfer-Encoding"), + U("Upgrade"), + U("Via"), + U("Warning"), + U("Allow"), + U("Content-Length"), + U("Content-Type"), + U("Content-Encoding"), + U("Content-Language"), + U("Content-Location"), + U("Content-MD5"), + U("Content-Range"), + U("Expires"), + U("Last-Modified"), + U("Accept"), + U("Accept-Charset"), + U("Accept-Encoding"), + U("Accept-Language"), + U("Authorization"), + U("Cookie"), + U("Expect"), + U("From"), + U("Host"), + U("If-Match"), + U("If-Modified-Since"), + U("If-None-Match"), + U("If-Range"), + U("If-Unmodified-Since"), + U("Max-Forwards"), + U("Proxy-Authorization"), + U("Referer"), + U("Range"), + U("TE"), + U("Translate"), + U("User-Agent") +}; static utility::string_t char_to_wstring(const char* src) { @@ -176,7 +167,7 @@ static std::map parse_http_headers(const H { if (headers.KnownHeaders[i].RawValueLength != 0) { - headers_map[HttpServerAPIKnownHeaders[i]] = char_to_wstring(headers.KnownHeaders[i].pRawValue); + headers_map[HttpServerAPIRequestKnownHeaders[i]] = char_to_wstring(headers.KnownHeaders[i].pRawValue); } } return headers_map; diff --git a/Release/tests/functional/json/CMakeLists.txt b/Release/tests/functional/json/CMakeLists.txt index 1d44a99ce6..25c90f29c1 100644 --- a/Release/tests/functional/json/CMakeLists.txt +++ b/Release/tests/functional/json/CMakeLists.txt @@ -15,16 +15,3 @@ if(UNIX AND NOT APPLE) cpprest_find_boost() target_link_libraries(json_test PRIVATE cpprestsdk_boost_internal) endif() - -if(MSVC) - get_target_property(_srcs json_test SOURCES) - - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/json-tests-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/json-tests-stdafx.pch") - endif() - - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fpjson-tests-stdafx.pch") - target_sources(json_test PRIVATE stdafx.cpp) - target_compile_options(json_test PRIVATE /Yustdafx.h /Fpjson-tests-stdafx.pch) -endif() diff --git a/Release/tests/functional/json/construction_tests.cpp b/Release/tests/functional/json/construction_tests.cpp index ee5d9cbbce..7a9275b301 100644 --- a/Release/tests/functional/json/construction_tests.cpp +++ b/Release/tests/functional/json/construction_tests.cpp @@ -11,7 +11,8 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ -#include "stdafx.h" +#include "cpprest/json.h" +#include "unittestpp.h" using namespace web; using namespace utility; @@ -49,6 +50,61 @@ SUITE(construction_tests) VERIFY_ARE_EQUAL(U("null"), arr[1].serialize()); } + TEST(int_assignment_op) + { + json::value v; + v = static_cast(1); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + + v = static_cast(1); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + + v = static_cast(1); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + + v = static_cast(1); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + + v = static_cast(1); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + + v = static_cast(1); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + } + + TEST(int_ctor) + { + { + json::value v(static_cast(1)); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + } + + { + json::value v(static_cast(1)); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + } + + { + json::value v(static_cast(1)); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + } + + { + json::value v(static_cast(1)); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + } + + { + json::value v(static_cast(1)); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + } + + { + json::value v(static_cast(1)); + VERIFY_ARE_EQUAL(U("1"), v.serialize()); + } + } + TEST(copy_ctor_array) { json::value arr = json::value::array(); diff --git a/Release/tests/functional/json/fuzz_tests.cpp b/Release/tests/functional/json/fuzz_tests.cpp index a925bf9dfb..80b9a81b04 100644 --- a/Release/tests/functional/json/fuzz_tests.cpp +++ b/Release/tests/functional/json/fuzz_tests.cpp @@ -10,10 +10,10 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ -#include "stdafx.h" - +#include "cpprest/json.h" #include "cpprest/containerstream.h" #include "cpprest/filestream.h" +#include "unittestpp.h" using namespace web; diff --git a/Release/tests/functional/json/iterator_tests.cpp b/Release/tests/functional/json/iterator_tests.cpp index e6c8e60ec3..0e72709aa3 100644 --- a/Release/tests/functional/json/iterator_tests.cpp +++ b/Release/tests/functional/json/iterator_tests.cpp @@ -11,8 +11,8 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ -#include "stdafx.h" - +#include "cpprest/json.h" +#include "unittestpp.h" #include using namespace web; diff --git a/Release/tests/functional/json/json_numbers_tests.cpp b/Release/tests/functional/json/json_numbers_tests.cpp index 4935a6af27..5fa90267ff 100644 --- a/Release/tests/functional/json/json_numbers_tests.cpp +++ b/Release/tests/functional/json/json_numbers_tests.cpp @@ -11,8 +11,8 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ -#include "stdafx.h" - +#include "cpprest/json.h" +#include "unittestpp.h" #include #include @@ -258,8 +258,8 @@ SUITE(json_numbers_tests) // unsigned int64 max oracleStream.precision(std::numeric_limits::digits10 + 2); - oracleStream << std::numeric_limits::max(); - json::value iMax(std::numeric_limits::max()); + oracleStream << (std::numeric_limits::max)(); + json::value iMax((std::numeric_limits::max)()); VERIFY_ARE_EQUAL(oracleStream.str(), iMax.serialize()); iMax.serialize(stream); VERIFY_ARE_EQUAL(oracleStream.str(), stream.str()); @@ -268,8 +268,8 @@ SUITE(json_numbers_tests) stream.str(U("")); oracleStream.str(U("")); oracleStream.clear(); - oracleStream << std::numeric_limits::min(); - json::value iMin(std::numeric_limits::min()); + oracleStream << (std::numeric_limits::min)(); + json::value iMin((std::numeric_limits::min)()); VERIFY_ARE_EQUAL(oracleStream.str(), iMin.serialize()); iMin.serialize(stream); VERIFY_ARE_EQUAL(oracleStream.str(), stream.str()); @@ -278,8 +278,8 @@ SUITE(json_numbers_tests) stream.str(U("")); oracleStream.str(U("")); oracleStream.precision(std::numeric_limits::digits10 + 2); - oracleStream << std::numeric_limits::max(); - json::value dMax(std::numeric_limits::max()); + oracleStream << (std::numeric_limits::max)(); + json::value dMax((std::numeric_limits::max)()); VERIFY_ARE_EQUAL(oracleStream.str(), dMax.serialize()); dMax.serialize(stream); VERIFY_ARE_EQUAL(oracleStream.str(), stream.str()); @@ -287,8 +287,8 @@ SUITE(json_numbers_tests) // double min stream.str(U("")); oracleStream.str(U("")); - oracleStream << std::numeric_limits::min(); - json::value dMin(std::numeric_limits::min()); + oracleStream << (std::numeric_limits::min)(); + json::value dMin((std::numeric_limits::min)()); VERIFY_ARE_EQUAL(oracleStream.str(), dMin.serialize()); dMin.serialize(stream); VERIFY_ARE_EQUAL(oracleStream.str(), stream.str()); diff --git a/Release/tests/functional/json/json_tests.h b/Release/tests/functional/json/json_tests.h deleted file mode 100644 index 2d0bac62d1..0000000000 --- a/Release/tests/functional/json/json_tests.h +++ /dev/null @@ -1,25 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * json_tests.h - * - * Common utilities and helper functions for JSON tests. - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#include "cpprest/json.h" -#include "unittestpp.h" - -namespace tests -{ -namespace functional -{ -namespace json_tests -{ -} -} // namespace functional -} // namespace tests diff --git a/Release/tests/functional/json/negative_parsing_tests.cpp b/Release/tests/functional/json/negative_parsing_tests.cpp index 33a678ab16..6c2ee9375f 100644 --- a/Release/tests/functional/json/negative_parsing_tests.cpp +++ b/Release/tests/functional/json/negative_parsing_tests.cpp @@ -10,7 +10,8 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ -#include "stdafx.h" +#include "cpprest/json.h" +#include "unittestpp.h" using namespace web; using namespace utility; diff --git a/Release/tests/functional/json/parsing_tests.cpp b/Release/tests/functional/json/parsing_tests.cpp index fc7a23779c..bd43ee253c 100644 --- a/Release/tests/functional/json/parsing_tests.cpp +++ b/Release/tests/functional/json/parsing_tests.cpp @@ -9,8 +9,8 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ -#include "stdafx.h" - +#include "cpprest/json.h" +#include "unittestpp.h" #include #include @@ -26,6 +26,144 @@ using namespace web; using namespace utility; using namespace utility::conversions; +static utility::string_t youtubeJson = _XPLATSTR( +R"delimeter({ + "kind": "youtube#playlistItemListResponse", + "etag": "\"Fznwjl6JEQdo1MGvHOGaz_YanRU/ranGcWzseanYs9xZ0NXAq24qK-w\"", + "pageInfo": { + "totalResults": 3, + "resultsPerPage": 5 + }, + "items": [ + { + "kind": "youtube#playlistItem", + "etag": "\"Fznwjl6JEQdo1MGvHOGaz_YanRU/phfRXORDKFrYjeJGWbI8MbIk08A\"", + "id": "VVVGMWhNVVZ3bHJ2bFZNalVHT1pFeGdnLm12RERIeEJyd1U4", + "snippet": { + "publishedAt": "2013-05-24T22:03:10.000Z", + "channelId": "UCF1hMUVwlrvlVMjUGOZExgg", + "title": "C++ REST SDK (\"Casablanca\")", + "description": "This library is a Microsoft effort to support cloud-based client-server communication in native code using a modern asynchronous C++ API design.", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/mvDDHxBrwU8/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/mvDDHxBrwU8/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/mvDDHxBrwU8/hqdefault.jpg", + "width": 480, + "height": 360 + }, + "standard": { + "url": "https://i.ytimg.com/vi/mvDDHxBrwU8/sddefault.jpg", + "width": 640, + "height": 480 + }, + "maxres": { + "url": "https://i.ytimg.com/vi/mvDDHxBrwU8/maxresdefault.jpg", + "width": 1280, + "height": 720 + } + }, + "channelTitle": "casablancacore", + "playlistId": "UUF1hMUVwlrvlVMjUGOZExgg", + "position": 0, + "resourceId": { + "kind": "youtube#video", + "videoId": "mvDDHxBrwU8" + } + } + }, + { + "kind": "youtube#playlistItem", + "etag": "\"Fznwjl6JEQdo1MGvHOGaz_YanRU/J65jYO0AIlbIqd4JpVigajlhVnE\"", + "id": "VVVGMWhNVVZ3bHJ2bFZNalVHT1pFeGdnLmlFVU9fdDhFYW5r", + "snippet": { + "publishedAt": "2013-05-07T18:47:24.000Z", + "channelId": "UCF1hMUVwlrvlVMjUGOZExgg", + "title": "C++ REST SDK", + "description": "A brief introduction to the C++ REST SDK. This video goes over high level concepts and features of the library. \n\nFor more information visit: http://casablanca.codeplex.com\nFor more information on PPL tasks visit: http://msdn.microsoft.com/en-us/library/dd492418.aspx", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/iEUO_t8Eank/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/iEUO_t8Eank/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/iEUO_t8Eank/hqdefault.jpg", + "width": 480, + "height": 360 + }, + "standard": { + "url": "https://i.ytimg.com/vi/iEUO_t8Eank/sddefault.jpg", + "width": 640, + "height": 480 + } + }, + "channelTitle": "casablancacore", + "playlistId": "UUF1hMUVwlrvlVMjUGOZExgg", + "position": 1, + "resourceId": { + "kind": "youtube#video", + "videoId": "iEUO_t8Eank" + } + } + }, + { + "kind": "youtube#playlistItem", + "etag": "\"Fznwjl6JEQdo1MGvHOGaz_YanRU/XMpuK2N4-LOhDWtgCG8nBw7eNl8\"", + "id": "VVVGMWhNVVZ3bHJ2bFZNalVHT1pFeGdnLk41cnlJN3U5RVFB", + "snippet": { + "publishedAt": "2013-05-02T21:24:56.000Z", + "channelId": "UCF1hMUVwlrvlVMjUGOZExgg", + "title": "bunny", + "description": "", + "thumbnails": { + "default": { + "url": "https://i.ytimg.com/vi/N5ryI7u9EQA/default.jpg", + "width": 120, + "height": 90 + }, + "medium": { + "url": "https://i.ytimg.com/vi/N5ryI7u9EQA/mqdefault.jpg", + "width": 320, + "height": 180 + }, + "high": { + "url": "https://i.ytimg.com/vi/N5ryI7u9EQA/hqdefault.jpg", + "width": 480, + "height": 360 + }, + "standard": { + "url": "https://i.ytimg.com/vi/N5ryI7u9EQA/sddefault.jpg", + "width": 640, + "height": 480 + } + }, + "channelTitle": "casablancacore", + "playlistId": "UUF1hMUVwlrvlVMjUGOZExgg", + "position": 2, + "resourceId": { + "kind": "youtube#video", + "videoId": "N5ryI7u9EQA" + } + } + } + ] +})delimeter" +); + namespace tests { namespace functional @@ -209,7 +347,7 @@ SUITE(parsing_tests) VERIFY_ARE_EQUAL(U("K"), str.as_string()); str = json::value::parse(U("\"\\u20AC\"")); - // Euro sign as a hexidecmial UTF-8 + // Euro sign as a hexadecimal UTF-8 const auto euro = to_string_t("\xE2\x82\xAC"); VERIFY_ARE_EQUAL(euro, str.as_string()); @@ -469,6 +607,12 @@ SUITE(parsing_tests) VERIFY_ARE_EQUAL(0u, arr.size()); } + TEST(bug_object_field_key_no_value) + { + VERIFY_PARSING_THROW(json::value::parse(U("{\"meow\"}"))); + VERIFY_PARSING_THROW(json::value::parse(U("{\"meow\": 42, \"purr\": 57, \"hiss\"}"))); + } + TEST(bug_416116) { json::value data2 = json::value::parse(U("\"δοκιμή\"")); @@ -487,30 +631,34 @@ SUITE(parsing_tests) TEST(byte_ptr_parsing_array) { char s[] = "[ \"test1\",true]"; + json::value v = json::value::parse(s); std::stringstream ss; ss << s; - json::value v = json::value::parse(ss); - auto s2 = v.serialize(); + json::value vv = json::value::parse(ss); + VERIFY_ARE_EQUAL(v, vv); + auto s2 = v.serialize(); VERIFY_ARE_EQUAL(s2, U("[\"test1\",true]")); std::stringstream os; - v.serialize(os); + vv.serialize(os); VERIFY_ARE_EQUAL(s2, to_string_t(os.str())); } TEST(byte_ptr_parsing_object) { char s[] = "{\"test1\":true }"; + json::value v = json::value::parse(s); std::stringstream ss; ss << s; - json::value v = json::value::parse(ss); - auto s2 = v.serialize(); + json::value vv = json::value::parse(ss); + VERIFY_ARE_EQUAL(v, vv); + auto s2 = v.serialize(); VERIFY_ARE_EQUAL(s2, U("{\"test1\":true}")); std::stringstream os; - v.serialize(os); + vv.serialize(os); VERIFY_ARE_EQUAL(s2, to_string_t(os.str())); } @@ -518,16 +666,18 @@ SUITE(parsing_tests) { utility::string_t ws = U("\"こんにちは\""); std::string s = to_utf8string(ws); + json::value v = json::value::parse(s); std::stringstream ss; ss << s; - json::value v = json::value::parse(ss); - auto s2 = v.serialize(); + json::value vv = json::value::parse(ss); + VERIFY_ARE_EQUAL(v, vv); + auto s2 = v.serialize(); VERIFY_ARE_EQUAL(s2, ws); std::stringstream os; - v.serialize(os); + vv.serialize(os); VERIFY_ARE_EQUAL(s2, to_string_t(os.str())); } @@ -756,6 +906,36 @@ SUITE(parsing_tests) #endif } + TEST(youtube_api) + { + auto v = json::value::parse(youtubeJson); + int count = 0; + auto& obj = v.as_object(); + + VERIFY_ARE_NOT_EQUAL(obj.find(U("pageInfo")), obj.end()); + VERIFY_ARE_NOT_EQUAL(obj.find(U("items")), obj.end()); + + auto& items = obj[U("items")]; + + for (auto iter = items.as_array().cbegin(); iter != items.as_array().cend(); ++iter) + { + const auto& item = *iter; + auto iSnippet = item.as_object().find(U("snippet")); + if (iSnippet == item.as_object().end()) + { + throw std::runtime_error("snippet key not found"); + } + auto iTitle = iSnippet->second.as_object().find(U("title")); + if (iTitle == iSnippet->second.as_object().end()) + { + throw std::runtime_error("title key not found"); + } + auto name = iTitle->second.serialize(); + count++; + } + VERIFY_ARE_EQUAL(3, count); // Update this accordingly, if the number of items changes + } + } // SUITE(parsing_tests) } // namespace json_tests diff --git a/Release/tests/functional/json/stdafx.cpp b/Release/tests/functional/json/stdafx.cpp deleted file mode 100644 index 48b9655134..0000000000 --- a/Release/tests/functional/json/stdafx.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - **/ -// stdafx.cpp : -// Include the standard header and generate the precompiled header. - -#include "stdafx.h" - -#if WIN32 -__declspec(dllexport) int json_test_generate_lib = 0; -#endif diff --git a/Release/tests/functional/json/stdafx.h b/Release/tests/functional/json/stdafx.h deleted file mode 100644 index bb65303dca..0000000000 --- a/Release/tests/functional/json/stdafx.h +++ /dev/null @@ -1,20 +0,0 @@ -/*** - * Copyright (C) Microsoft. All rights reserved. - * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - * - * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - * - * stdafx.h - * - * Pre-compiled headers - * - * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - ****/ - -#pragma once - -#include "unittestpp.h" -#include - -#define NOMINMAX -#include "json_tests.h" diff --git a/Release/tests/functional/json/to_as_and_operators_tests.cpp b/Release/tests/functional/json/to_as_and_operators_tests.cpp index 6103adf6a4..cbf2a92e04 100644 --- a/Release/tests/functional/json/to_as_and_operators_tests.cpp +++ b/Release/tests/functional/json/to_as_and_operators_tests.cpp @@ -9,7 +9,8 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ -#include "stdafx.h" +#include "cpprest/json.h" +#include "unittestpp.h" using namespace web; using namespace utility; diff --git a/Release/tests/functional/misc/atl_headers/header_test1.cpp b/Release/tests/functional/misc/atl_headers/header_test1.cpp index 46d54ff2e3..95dd22359d 100644 --- a/Release/tests/functional/misc/atl_headers/header_test1.cpp +++ b/Release/tests/functional/misc/atl_headers/header_test1.cpp @@ -11,7 +11,6 @@ // Include ATL headers before casablanca headers #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#define NOMINMAX #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit #ifndef VC_EXTRALEAN diff --git a/Release/tests/functional/misc/atl_headers/header_test2.cpp b/Release/tests/functional/misc/atl_headers/header_test2.cpp index 91cf63fbc6..73e35711de 100644 --- a/Release/tests/functional/misc/atl_headers/header_test2.cpp +++ b/Release/tests/functional/misc/atl_headers/header_test2.cpp @@ -9,7 +9,6 @@ * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#define NOMINMAX #include "cpprest/http_client.h" // Include ATL headers after casablanca headers diff --git a/Release/tests/functional/pplx/pplx_test/CMakeLists.txt b/Release/tests/functional/pplx/pplx_test/CMakeLists.txt index 0e2672b73e..7007a5829a 100644 --- a/Release/tests/functional/pplx/pplx_test/CMakeLists.txt +++ b/Release/tests/functional/pplx/pplx_test/CMakeLists.txt @@ -6,15 +6,4 @@ set(SOURCES add_casablanca_test(pplx_test SOURCES) -if(MSVC) - get_target_property(_srcs pplx_test SOURCES) - - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/pplx-tests-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/pplx-tests-stdafx.pch") - endif() - - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fppplx-tests-stdafx.pch") - target_sources(pplx_test PRIVATE stdafx.cpp) - target_compile_options(pplx_test PRIVATE /Yustdafx.h /Fppplx-tests-stdafx.pch) -endif() +configure_pch(pplx_test stdafx.h stdafx.cpp) diff --git a/Release/tests/functional/pplx/pplx_test/stdafx.h b/Release/tests/functional/pplx/pplx_test/stdafx.h index 7c2d53ab4f..790829ef64 100644 --- a/Release/tests/functional/pplx/pplx_test/stdafx.h +++ b/Release/tests/functional/pplx/pplx_test/stdafx.h @@ -12,7 +12,6 @@ #pragma once #ifdef _WIN32 -#define NOMINMAX #include #endif diff --git a/Release/tests/functional/streams/CMakeLists.txt b/Release/tests/functional/streams/CMakeLists.txt index 29d09bbac8..92077b0228 100644 --- a/Release/tests/functional/streams/CMakeLists.txt +++ b/Release/tests/functional/streams/CMakeLists.txt @@ -24,15 +24,4 @@ if(NOT WIN32 OR CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp") endif() endif() -if(MSVC) - get_target_property(_srcs streams_test SOURCES) - - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/streams-tests-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/streams-tests-stdafx.pch") - endif() - - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fpstreams-tests-stdafx.pch") - target_sources(streams_test PRIVATE stdafx.cpp) - target_compile_options(streams_test PRIVATE /Yustdafx.h /Fpstreams-tests-stdafx.pch) -endif() +configure_pch(streams_test stdafx.h stdafx.cpp) diff --git a/Release/tests/functional/streams/istream_tests.cpp b/Release/tests/functional/streams/istream_tests.cpp index d1018e3116..32cb545aa2 100644 --- a/Release/tests/functional/streams/istream_tests.cpp +++ b/Release/tests/functional/streams/istream_tests.cpp @@ -1333,7 +1333,7 @@ SUITE(istream_tests) const auto actual = istream_double.extract().get(); compare_double(expected, actual); - if (actual <= std::numeric_limits::max()) + if (actual <= (std::numeric_limits::max)()) compare_float(float(expected), istream_float.extract().get()); else VERIFY_THROWS(istream_float.extract().get(), std::exception); diff --git a/Release/tests/functional/streams/memstream_tests.cpp b/Release/tests/functional/streams/memstream_tests.cpp index 6442883327..3bdbd6812a 100644 --- a/Release/tests/functional/streams/memstream_tests.cpp +++ b/Release/tests/functional/streams/memstream_tests.cpp @@ -13,7 +13,6 @@ #include #endif #ifdef _WIN32 -#define NOMINMAX #include #endif diff --git a/Release/tests/functional/uri/CMakeLists.txt b/Release/tests/functional/uri/CMakeLists.txt index e3f7de1da2..298ec21b01 100644 --- a/Release/tests/functional/uri/CMakeLists.txt +++ b/Release/tests/functional/uri/CMakeLists.txt @@ -13,15 +13,4 @@ set(SOURCES add_casablanca_test(uri_test SOURCES) -if(MSVC) - get_target_property(_srcs uri_test SOURCES) - - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/uri-tests-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/uri-tests-stdafx.pch") - endif() - - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fpuri-tests-stdafx.pch") - target_sources(uri_test PRIVATE stdafx.cpp) - target_compile_options(uri_test PRIVATE /Yustdafx.h /Fpuri-tests-stdafx.pch) -endif() +configure_pch(uri_test stdafx.h stdafx.cpp) diff --git a/Release/tests/functional/uri/constructor_tests.cpp b/Release/tests/functional/uri/constructor_tests.cpp index ea6041c26a..ffcf5ada27 100644 --- a/Release/tests/functional/uri/constructor_tests.cpp +++ b/Release/tests/functional/uri/constructor_tests.cpp @@ -24,6 +24,11 @@ namespace uri_tests { SUITE(constructor_tests) { + TEST(not_really_a_loopback_uri) + { + uri u(uri::encode_uri(U("https://127.evil.com"))); + VERIFY_IS_FALSE(u.is_host_loopback()); + } TEST(parsing_constructor_char) { uri u(uri::encode_uri(U("net.tcp://steve:@testname.com:81/bleh%?qstring#goo"))); diff --git a/Release/tests/functional/uri/diagnostic_tests.cpp b/Release/tests/functional/uri/diagnostic_tests.cpp index d8fb45d91c..3271898f60 100644 --- a/Release/tests/functional/uri/diagnostic_tests.cpp +++ b/Release/tests/functional/uri/diagnostic_tests.cpp @@ -82,7 +82,7 @@ SUITE(diagnostic_tests) VERIFY_IS_FALSE(uri(U("http://bleh/?qstring")).is_host_loopback()); VERIFY_IS_FALSE(uri(U("http://+*/?qstring")).is_host_loopback()); VERIFY_IS_TRUE(uri(U("http://127.0.0.1/")).is_host_loopback()); - VERIFY_IS_TRUE(uri(U("http://127.155.0.1/")).is_host_loopback()); + VERIFY_IS_FALSE(uri(U("http://127.155.0.1/")).is_host_loopback()); VERIFY_IS_FALSE(uri(U("http://128.0.0.1/")).is_host_loopback()); } diff --git a/Release/tests/functional/utils/CMakeLists.txt b/Release/tests/functional/utils/CMakeLists.txt index 201af77039..a8f157ecdb 100644 --- a/Release/tests/functional/utils/CMakeLists.txt +++ b/Release/tests/functional/utils/CMakeLists.txt @@ -13,15 +13,4 @@ if(CMAKE_COMPILER_IS_GNUCXX) target_compile_options(utils_test PRIVATE "-Wno-deprecated-declarations") endif() -if(MSVC) - get_target_property(_srcs utils_test SOURCES) - - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") - set_property(SOURCE stdafx.cpp APPEND PROPERTY OBJECT_OUTPUTS "${CMAKE_CURRENT_BINARY_DIR}/utils-tests-stdafx.pch") - set_property(SOURCE ${_srcs} APPEND PROPERTY OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/utils-tests-stdafx.pch") - endif() - - set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h /Fputils-tests-stdafx.pch") - target_sources(utils_test PRIVATE stdafx.cpp) - target_compile_options(utils_test PRIVATE /Yustdafx.h /Fputils-tests-stdafx.pch) -endif() +configure_pch(utils_test stdafx.h stdafx.cpp) diff --git a/Release/tests/functional/utils/datetime.cpp b/Release/tests/functional/utils/datetime.cpp index 33d532a098..22954ca942 100644 --- a/Release/tests/functional/utils/datetime.cpp +++ b/Release/tests/functional/utils/datetime.cpp @@ -10,6 +10,7 @@ ****/ #include "stdafx.h" + #include #include @@ -81,6 +82,10 @@ SUITE(datetime) auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601); utility::string_t str2 = dt.to_string(utility::datetime::ISO_8601); VERIFY_ARE_EQUAL(str2, strExpected); + + auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::ISO_8601); + utility::string_t str3 = dt_me.to_string(utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(str3, strExpected); } void TestDateTimeRoundtrip(utility::string_t str) { TestDateTimeRoundtrip(str, str); } @@ -123,17 +128,26 @@ SUITE(datetime) TestDateTimeRoundtrip(_XPLATSTR("2013-11-19T14:30:59.5Z")); } - TEST(parsing_time_roundtrip_year_1900) - { - TestDateTimeRoundtrip(_XPLATSTR("1900-01-01T00:00:00Z")); - } + TEST(parsing_time_roundtrip_year_1900) { TestDateTimeRoundtrip(_XPLATSTR("1900-01-01T00:00:00Z")); } - TEST(parsing_time_roundtrip_year_9999) - { - TestDateTimeRoundtrip(_XPLATSTR("9999-12-31T23:59:59Z")); - } + TEST(parsing_time_roundtrip_year_9999) { TestDateTimeRoundtrip(_XPLATSTR("9999-12-31T23:59:59Z")); } + + TEST(parsing_time_roundtrip_year_2016) { TestDateTimeRoundtrip(_XPLATSTR("2016-12-31T20:59:59Z")); } + + TEST(parsing_time_roundtrip_year_2020) { TestDateTimeRoundtrip(_XPLATSTR("2020-12-31T20:59:59Z")); } - TEST(emitting_time_correct_day) { + TEST(parsing_time_roundtrip_year_2021) { TestDateTimeRoundtrip(_XPLATSTR("2021-01-01T20:59:59Z")); } + + TEST(parsing_time_roundtrip_year_1601) { TestDateTimeRoundtrip(_XPLATSTR("1601-01-01T00:00:00Z")); } + + TEST(parsing_time_roundtrip_year_1602) { TestDateTimeRoundtrip(_XPLATSTR("1602-01-01T00:00:00Z")); } + + TEST(parsing_time_roundtrip_year_1603) { TestDateTimeRoundtrip(_XPLATSTR("1603-01-01T00:00:00Z")); } + + TEST(parsing_time_roundtrip_year_1604) { TestDateTimeRoundtrip(_XPLATSTR("1604-01-01T00:00:00Z")); } + + TEST(emitting_time_correct_day) + { const auto test = utility::datetime() + UINT64_C(132004507640000000); // 2019-04-22T23:52:44 is a Monday const auto actual = test.to_string(utility::datetime::RFC_1123); const utility::string_t expected(_XPLATSTR("Mon")); @@ -281,13 +295,13 @@ SUITE(datetime) _XPLATSTR("Thu, 01 Jan 1970 00:00:00 G"), _XPLATSTR("Thu, 01 Jan 1970 00:00:00 GM"), _XPLATSTR("Fri, 01 Jan 1970 00:00:00 GMT"), // wrong day - _XPLATSTR("01 Jan 1899 00:00:00 GMT"), // year too small - _XPLATSTR("01 Xxx 1971 00:00:00 GMT"), // month bad - _XPLATSTR("00 Jan 1971 00:00:00 GMT"), // day too small - _XPLATSTR("32 Jan 1971 00:00:00 GMT"), // day too big - _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb - _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb (non-leap year) - _XPLATSTR("32 Mar 1971 00:00:00 GMT"), // other months + _XPLATSTR("01 Jan 1600 00:00:00 GMT"), // year too small + _XPLATSTR("01 Xxx 1971 00:00:00 GMT"), // month bad + _XPLATSTR("00 Jan 1971 00:00:00 GMT"), // day too small + _XPLATSTR("32 Jan 1971 00:00:00 GMT"), // day too big + _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb + _XPLATSTR("30 Feb 1971 00:00:00 GMT"), // day too big for feb (non-leap year) + _XPLATSTR("32 Mar 1971 00:00:00 GMT"), // other months _XPLATSTR("31 Apr 1971 00:00:00 GMT"), _XPLATSTR("32 May 1971 00:00:00 GMT"), _XPLATSTR("31 Jun 1971 00:00:00 GMT"), @@ -302,9 +316,10 @@ SUITE(datetime) _XPLATSTR("01 Jan 1971 00:60:00 GMT"), // minute too big _XPLATSTR("01 Jan 1971 00:00:70 GMT"), // second too big _XPLATSTR("01 Jan 1971 00:00:61 GMT"), - _XPLATSTR("01 Jan 1899 00:00:00 GMT"), // underflow - _XPLATSTR("01 Jan 1969 00:00:00 CEST"), // bad tz - _XPLATSTR("01 Jan 1970 00:00:00 +2400"), // bad tzoffsets + _XPLATSTR("01 Jan 1600 00:00:00 GMT"), // underflow + _XPLATSTR("01 Jan 1969 00:00:00 CEST"), // bad tz + _XPLATSTR("14 Jan 2019 23:16:21 G0100"), // bad tzoffsets + _XPLATSTR("01 Jan 1970 00:00:00 +2400"), _XPLATSTR("01 Jan 1970 00:00:00 -3000"), _XPLATSTR("01 Jan 1970 00:00:00 +2160"), _XPLATSTR("01 Jan 1970 00:00:00 -2400"), @@ -316,6 +331,8 @@ SUITE(datetime) { auto dt = utility::datetime::from_string(str, utility::datetime::RFC_1123); VERIFY_ARE_EQUAL(0, dt.to_interval()); + auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::RFC_1123); + VERIFY_ARE_EQUAL(utility::datetime::maximum(), dt_me); } } @@ -445,7 +462,7 @@ SUITE(datetime) _XPLATSTR("1970-01-01T00:00:"), _XPLATSTR("1970-01-01T00:00:0"), // _XPLATSTR("1970-01-01T00:00:00"), // accepted as invalid timezone above - _XPLATSTR("1899-01-01T00:00:00Z"), // year too small + _XPLATSTR("1600-01-01T00:00:00Z"), // year too small _XPLATSTR("1971-00-01T00:00:00Z"), // month too small _XPLATSTR("1971-20-01T00:00:00Z"), // month too big _XPLATSTR("1971-13-01T00:00:00Z"), @@ -468,8 +485,8 @@ SUITE(datetime) _XPLATSTR("1971-01-01T00:60:00Z"), // minute too big _XPLATSTR("1971-01-01T00:00:70Z"), // second too big _XPLATSTR("1971-01-01T00:00:61Z"), - _XPLATSTR("1899-01-01T00:00:00Z"), // underflow - _XPLATSTR("1900-01-01T00:00:00+00:01"), // time zone underflow + _XPLATSTR("1600-01-01T00:00:00Z"), // underflow + _XPLATSTR("1601-01-01T00:00:00+00:01"), // time zone underflow // _XPLATSTR("1970-01-01T00:00:00.Z"), // accepted as invalid timezone above _XPLATSTR("1970-01-01T00:00:00+24:00"), // bad tzoffsets _XPLATSTR("1970-01-01T00:00:00-30:00"), @@ -483,9 +500,55 @@ SUITE(datetime) { auto dt = utility::datetime::from_string(str, utility::datetime::ISO_8601); VERIFY_ARE_EQUAL(dt.to_interval(), 0); + auto dt_me = utility::datetime::from_string_maximum_error(str, utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(dt_me, utility::datetime::maximum()); } } + TEST(can_emit_nt_epoch_zero_rfc_1123) + { + auto result = utility::datetime {}.to_string(utility::datetime::RFC_1123); + VERIFY_ARE_EQUAL(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), result); + } + + TEST(can_emit_nt_epoch_zero_iso_8601) + { + auto result = utility::datetime {}.to_string(utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(_XPLATSTR("1601-01-01T00:00:00Z"), result); + } + + TEST(can_emit_year_9999_rfc_1123) + { + auto result = + utility::datetime::from_interval(INT64_C(0x24C85A5ED1C018F0)).to_string(utility::datetime::RFC_1123); + VERIFY_ARE_EQUAL(_XPLATSTR("Fri, 31 Dec 9999 23:59:59 GMT"), result); + } + + TEST(can_emit_year_9999_iso_8601) + { + auto result = + utility::datetime::from_interval(INT64_C(0x24C85A5ED1C018F0)).to_string(utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(_XPLATSTR("9999-12-31T23:59:59.999Z"), result); + } + + TEST(can_parse_nt_epoch_zero_rfc_1123) + { + auto dt = + utility::datetime::from_string(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), utility::datetime::RFC_1123); + VERIFY_ARE_EQUAL(0U, dt.to_interval()); + auto dt_me = utility::datetime::from_string_maximum_error(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), + utility::datetime::RFC_1123); + VERIFY_ARE_EQUAL(0U, dt_me.to_interval()); + } + + TEST(can_parse_nt_epoch_zero_iso_8601) + { + auto dt = utility::datetime::from_string(_XPLATSTR("1601-01-01T00:00:00Z"), utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(0U, dt.to_interval()); + auto dt_me = utility::datetime::from_string_maximum_error(_XPLATSTR("1601-01-01T00:00:00Z"), + utility::datetime::ISO_8601); + VERIFY_ARE_EQUAL(0U, dt_me.to_interval()); + } } // SUITE(datetime) } // namespace utils_tests diff --git a/Release/tests/functional/utils/win32_encryption_tests.cpp b/Release/tests/functional/utils/win32_encryption_tests.cpp index 32e6ab2ecb..a2be7cde5a 100644 --- a/Release/tests/functional/utils/win32_encryption_tests.cpp +++ b/Release/tests/functional/utils/win32_encryption_tests.cpp @@ -21,7 +21,7 @@ namespace functional { namespace utils_tests { -#if defined(_WIN32) && !defined(CPPREST_TARGET_XP) && !defined(__cplusplus_winrt) +#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt) SUITE(win32_encryption) { TEST(win32_encryption_random_string) @@ -42,7 +42,7 @@ SUITE(win32_encryption) } // SUITE(win32_encryption) -#endif +#endif // defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA && !defined(__cplusplus_winrt) } // namespace utils_tests } // namespace functional diff --git a/Release/tests/functional/websockets/client/authentication_tests.cpp b/Release/tests/functional/websockets/client/authentication_tests.cpp index 2953686042..a35949c9f3 100644 --- a/Release/tests/functional/websockets/client/authentication_tests.cpp +++ b/Release/tests/functional/websockets/client/authentication_tests.cpp @@ -128,83 +128,6 @@ SUITE(authentication_tests) } } -// These tests are specific to our websocketpp based implementation. -#if !defined(__cplusplus_winrt) - - void sni_test_impl(websocket_client & client) - { - try - { - client.connect(U("wss://swordsoftruth.com")).wait(); - - // Should never be reached. - VERIFY_IS_TRUE(false); - } - catch (const websocket_exception& e) - { - if (is_timeout(e.what())) - { - // Since this test depends on an outside server sometimes it sporadically can fail due to timeouts - // especially on our build machines. - return; - } - - // This test just covers establishing the TLS connection and verifying - // the server certificate, expect it to return an unexpected HTTP status code. - if (e.error_code().value() == 20) - { - return; - } - throw; - } - } - - // Test specifically for server SignalR team hit interesting cases with. - TEST(sni_with_older_server_test) - { - websocket_client client; - sni_test_impl(client); - } - - // WinRT doesn't expose option for disabling. - // No stable server is available to reliably test this. - // The configuration below relies on a timeout in the success case. - TEST(disable_sni, "Ignore", "Manual") - { - websocket_client_config config; - config.set_server_name("expired.badssl.com"); - config.disable_sni(); - websocket_client client(config); - - try - { - client.connect(U("wss://badssl.com")).wait(); - - // Should never be reached. - VERIFY_IS_TRUE(false); - } - catch (const websocket_exception& e) - { - // Should fail for a reason different than invalid HTTP status. - if (e.error_code().value() != 20) - { - return; - } - throw; - } - } - - // Winrt doesn't allow explicitly setting server host for SNI. - TEST(sni_explicit_hostname) - { - websocket_client_config config; - const auto& name = utf8string("swordsoftruth.com"); - config.set_server_name(name); - VERIFY_ARE_EQUAL(name, config.server_name()); - websocket_client client(config); - sni_test_impl(client); - } - void handshake_error_test_impl(const ::utility::string_t& host) { websocket_client client; @@ -231,8 +154,6 @@ SUITE(authentication_tests) TEST(cert_expired) { handshake_error_test_impl(U("wss://expired.badssl.com/")); } -#endif - } // SUITE(authentication_tests) } // namespace client diff --git a/Release/tests/functional/websockets/client/send_msg_tests.cpp b/Release/tests/functional/websockets/client/send_msg_tests.cpp index e99bccd03c..eed1dedf94 100644 --- a/Release/tests/functional/websockets/client/send_msg_tests.cpp +++ b/Release/tests/functional/websockets/client/send_msg_tests.cpp @@ -104,16 +104,32 @@ SUITE(send_msg_tests) } template - pplx::task send_pong_msg_helper(SocketClientClass & client, web::uri uri, test_websocket_server & server) + pplx::task send_ping_msg_helper(SocketClientClass & client, web::uri uri, test_websocket_server & server, + const std::string& body = "") { server.next_message( - [](test_websocket_msg msg) // Handler to verify the message sent by the client. - { websocket_asserts::assert_message_equals(msg, "", test_websocket_message_type::WEB_SOCKET_PONG_TYPE); }); + [body](test_websocket_msg msg) // Handler to verify the message sent by the client. + { websocket_asserts::assert_message_equals(msg, body, test_websocket_message_type::WEB_SOCKET_PING_TYPE); }); client.connect(uri).wait(); websocket_outgoing_message msg; - msg.set_pong_message(); + msg.set_ping_message(body); + return client.send(msg); + } + + template + pplx::task send_pong_msg_helper(SocketClientClass & client, web::uri uri, test_websocket_server & server, + const std::string& body = "") + { + server.next_message( + [body](test_websocket_msg msg) // Handler to verify the message sent by the client. + { websocket_asserts::assert_message_equals(msg, body, test_websocket_message_type::WEB_SOCKET_PONG_TYPE); }); + + client.connect(uri).wait(); + + websocket_outgoing_message msg; + msg.set_pong_message(body); return client.send(msg); } @@ -493,6 +509,24 @@ SUITE(send_msg_tests) } #if !defined(__cplusplus_winrt) + // Send a ping message to the server + TEST_FIXTURE(uri_address, send_ping_msg) + { + test_websocket_server server; + websocket_client client; + send_ping_msg_helper(client, m_uri, server).wait(); + client.close().wait(); + } + + // Send a ping message to the server with a body + TEST_FIXTURE(uri_address, send_ping_msg_body) + { + test_websocket_server server; + websocket_client client; + send_ping_msg_helper(client, m_uri, server, "abcdefghijklmnopqrstuvwxyz").wait(); + client.close().wait(); + } + // Send an unsolicited pong message to the server TEST_FIXTURE(uri_address, send_pong_msg) { @@ -502,6 +536,15 @@ SUITE(send_msg_tests) client.close().wait(); } + // Send an unsolicited pong message to the server with a body + TEST_FIXTURE(uri_address, send_pong_msg_body) + { + test_websocket_server server; + websocket_client client; + send_pong_msg_helper(client, m_uri, server, "abcdefghijklmnopqrstuvwxyz").wait(); + client.close().wait(); + } + // Send an unsolicited pong message to the server with websocket_callback_client TEST_FIXTURE(uri_address, send_pong_msg_callback_client) { diff --git a/Release/tests/functional/websockets/utilities/stdafx.h b/Release/tests/functional/websockets/utilities/stdafx.h index e8adb749c9..0c6e35125a 100644 --- a/Release/tests/functional/websockets/utilities/stdafx.h +++ b/Release/tests/functional/websockets/utilities/stdafx.h @@ -13,7 +13,6 @@ #if defined(_WIN32) // Include first to avoid any issues with Windows.h. -#define NOMINMAX #include #endif diff --git a/Release/tests/functional/websockets/utilities/test_websocket_server.cpp b/Release/tests/functional/websockets/utilities/test_websocket_server.cpp index 151d03bca0..863376f22a 100644 --- a/Release/tests/functional/websockets/utilities/test_websocket_server.cpp +++ b/Release/tests/functional/websockets/utilities/test_websocket_server.cpp @@ -122,6 +122,20 @@ class _test_websocket_server m_server_connected.set_exception(std::runtime_error("Connection attempt failed.")); }); + m_srv.set_ping_handler([this](websocketpp::connection_hdl hdl, std::string input) { + auto fn = m_test_srv->get_next_message_handler(); + assert(fn); + + test_websocket_msg wsmsg; + + wsmsg.set_data(std::vector(input.begin(), input.end())); + + wsmsg.set_msg_type(WEB_SOCKET_PING_TYPE); + fn(wsmsg); + + return true; + }); + m_srv.set_pong_handler([this](websocketpp::connection_hdl hdl, std::string input) { auto fn = m_test_srv->get_next_message_handler(); assert(fn); diff --git a/Release/tests/functional/websockets/utilities/test_websocket_server.h b/Release/tests/functional/websockets/utilities/test_websocket_server.h index 50489e1a9d..23fbea12e2 100644 --- a/Release/tests/functional/websockets/utilities/test_websocket_server.h +++ b/Release/tests/functional/websockets/utilities/test_websocket_server.h @@ -46,6 +46,7 @@ enum test_websocket_message_type WEB_SOCKET_UTF8_MESSAGE_TYPE, WEB_SOCKET_UTF8_FRAGMENT_TYPE, WEB_SOCKET_CLOSE_TYPE, + WEB_SOCKET_PING_TYPE, WEB_SOCKET_PONG_TYPE }; diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..869fdfe2b2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/azure-devops/build-ubuntu-apt.yml b/azure-devops/build-ubuntu-apt.yml new file mode 100644 index 0000000000..ae3844edb3 --- /dev/null +++ b/azure-devops/build-ubuntu-apt.yml @@ -0,0 +1,30 @@ +parameters: + name: 'Ubuntu_1604_Apt' + image: 'Ubuntu 16.04' + +jobs: +- job: ${{ parameters.name }} + pool: + vmImage: ${{ parameters.image }} + steps: + - script: | + sudo apt -y remove php* + sudo apt install -y ppa-purge + sudo ppa-purge -y ppa:ondrej/php + unset BOOST_ROOT + sudo apt install -y libboost-atomic-dev libboost-thread-dev libboost-system-dev libboost-date-time-dev libboost-regex-dev libboost-filesystem-dev libboost-random-dev libboost-chrono-dev libboost-serialization-dev libwebsocketpp-dev openssl libssl-dev ninja-build + mkdir build.debug + cd build.debug + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug .. + cd .. + mkdir build.release + cd build.release + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .. + cd .. + ninja -C build.debug + ninja -C build.release + cd build.debug/Release/Binaries + ./test_runner *test.so + cd ../../../build.release/Release/Binaries + ./test_runner *test.so + displayName: Run build diff --git a/azure-devops/build-ubuntu-vcpkg.yml b/azure-devops/build-ubuntu-vcpkg.yml new file mode 100644 index 0000000000..aeae2b7ea5 --- /dev/null +++ b/azure-devops/build-ubuntu-vcpkg.yml @@ -0,0 +1,45 @@ +parameters: + name: 'Ubuntu_1604_Vcpkg' + image: 'Ubuntu 16.04' + +jobs: +- job: ${{ parameters.name }} + pool: + vmImage: ${{ parameters.image }} + steps: + - script: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt -y update + sudo apt install g++-9 ninja-build -y + git submodule update --init vcpkg + ./vcpkg/bootstrap-vcpkg.sh + ./vcpkg/vcpkg install zlib openssl boost-locale boost-system boost-date-time boost-regex websocketpp boost-thread boost-filesystem boost-random boost-chrono boost-interprocess brotli --vcpkg-root ./vcpkg + displayName: Vcpkg install dependencies + - script: | + mkdir build.debug + mkdir build.release + displayName: Make Build Directories + - task: CMake@1 + inputs: + workingDirectory: 'build.debug' + cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' + - task: CMake@1 + inputs: + workingDirectory: 'build.release' + cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' + - script: | + cd build.debug + ninja + displayName: 'Run ninja debug' + - script: | + cd build.debug/Release/Binaries + ./test_runner *test.so + displayName: 'Run Tests debug' + - script: | + cd build.release + ninja + displayName: 'Run ninja, release' + - script: | + cd build.release/Release/Binaries + ./test_runner *test.so + displayName: 'Run tests, release' diff --git a/azure-devops/build-windows.yml b/azure-devops/build-windows.yml new file mode 100644 index 0000000000..45045ee907 --- /dev/null +++ b/azure-devops/build-windows.yml @@ -0,0 +1,47 @@ +parameters: + name: 'Windows_VS2019_x86' + targetPlatform: 'x86' + image: 'windows-latest' + +jobs: +- job: ${{ parameters.name }} + pool: + vmImage: ${{ parameters.image }} + variables: + vcpkgLocation: '$(Build.SourcesDirectory)/vcpkg' + vcpkgResponseFile: $(Build.SourcesDirectory)/azure-devops/vcpkg-windows.txt + steps: + - script: git submodule update --init vcpkg + displayName: Checkout vcpkg submodule + - task: run-vcpkg@0 + displayName: 'Run vcpkg' + inputs: + vcpkgArguments: '@$(vcpkgResponseFile)' + vcpkgDirectory: '$(vcpkgLocation)' + vcpkgTriplet: ${{ parameters.targetPlatform }}-windows + - task: run-cmake@0 + displayName: 'Run CMake with Ninja (Debug)' + enabled: true + inputs: + cmakeListsOrSettingsJson: 'CMakeListsTxtBasic' + cmakeBuildType: 'Debug' + useVcpkgToolchainFile: true + buildDirectory: $(Build.ArtifactStagingDirectory)/${{ parameters.targetPlatform }}_Debug + cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF' + - script: | + cd $(Build.ArtifactStagingDirectory)\${{ parameters.targetPlatform }}_Debug\Release\Binaries + .\test_runner.exe *testd.dll + displayName: 'Run tests, debug' + - task: run-cmake@0 + displayName: 'Run CMake with Ninja (Release)' + enabled: true + inputs: + cmakeListsOrSettingsJson: 'CMakeListsTxtBasic' + cmakeBuildType: 'Release' + useVcpkgToolchainFile: true + buildDirectory: $(Build.ArtifactStagingDirectory)/${{ parameters.targetPlatform }}_Release + cmakeAppendedArgs: '-DCPPREST_EXCLUDE_BROTLI=OFF' + - script: | + cd $(Build.ArtifactStagingDirectory)\${{ parameters.targetPlatform }}_Release\Release\Binaries + .\test_runner.exe *test.dll + displayName: 'Run tests, release' diff --git a/azure-devops/vcpkg-windows.txt b/azure-devops/vcpkg-windows.txt new file mode 100644 index 0000000000..582d18dcdb --- /dev/null +++ b/azure-devops/vcpkg-windows.txt @@ -0,0 +1,7 @@ +openssl +boost-system +boost-date-time +boost-regex +boost-interprocess +websocketpp +brotli diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 23cd6f03ff..b83222df04 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,74 +1,32 @@ # CppRestSdk Azure Pipelines Configuration jobs: - - job: Windows_VS2017_x86 + - template: azure-devops/build-windows.yml + parameters: + name: 'Windows_VS2019_x86' + targetPlatform: x86 + image: 'windows-latest' + - template: azure-devops/build-windows.yml + parameters: + name: 'Windows_VS2019_x64' + targetPlatform: x64 + image: 'windows-latest' + - template: azure-devops/build-windows.yml + parameters: + name: 'Windows_VS2017_x86' + targetPlatform: x86 + image: 'vs2017-win2016' + - template: azure-devops/build-windows.yml + parameters: + name: 'Windows_VS2017_x64' + targetPlatform: x64 + image: 'vs2017-win2016' + - job: Windows_VS2019_UWP pool: - vmImage: 'vs2017-win2016' - steps: - - script: .\vcpkg\bootstrap-vcpkg.bat - displayName: Bootstrap vcpkg - - script: .\vcpkg\vcpkg.exe install zlib openssl boost-system boost-date-time boost-regex boost-interprocess websocketpp brotli --vcpkg-root .\vcpkg - displayName: vcpkg install dependencies - - script: mkdir build.common - displayName: Make Build Directory - - task: CMake@1 - inputs: - workingDirectory: 'build.common' - cmakeArgs: '-DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake -DCPPREST_EXCLUDE_BROTLI=OFF ..' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - - script: | - cd build.common\Release\Binaries\Debug - .\test_runner.exe *testd.dll - displayName: 'Run tests, debug' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - configuration: 'Release' - - script: | - cd build.common\Release\Binaries\Release - .\test_runner.exe *test.dll - displayName: 'Run tests, release' - - job: Windows_VS2017_x64 - pool: - vmImage: 'vs2017-win2016' - steps: - - script: .\vcpkg\bootstrap-vcpkg.bat - displayName: Bootstrap vcpkg - - script: .\vcpkg\vcpkg.exe install zlib openssl boost-system boost-date-time boost-regex boost-interprocess websocketpp brotli --triplet x64-windows --vcpkg-root .\vcpkg - displayName: vcpkg install dependencies - - script: mkdir build.common - displayName: Make Build Directory - - task: CMake@1 - inputs: - workingDirectory: 'build.common' - cmakeArgs: '-A x64 -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake -DCPPREST_EXCLUDE_BROTLI=OFF ..' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - platform: 'x64' - - script: | - cd build.common\Release\Binaries\Debug - .\test_runner.exe *testd.dll - displayName: 'Run tests, debug' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - platform: 'x64' - configuration: 'Release' - - script: | - cd build.common\Release\Binaries\Release - .\test_runner.exe *test.dll - displayName: 'Run tests, release' - - job: Windows_VS2017_UWP - pool: - vmImage: 'vs2017-win2016' + vmImage: 'windows-latest' steps: + - script: git submodule update --init vcpkg + displayName: Checkout vcpkg submodule - script: .\vcpkg\bootstrap-vcpkg.bat displayName: Bootstrap vcpkg - script: .\vcpkg\vcpkg.exe install zlib --triplet x64-uwp --vcpkg-root .\vcpkg @@ -84,146 +42,22 @@ jobs: solution: 'build.common/ALL_BUILD.vcxproj' maximumCpuCount: true platform: 'x64' - - job: Windows_VS2015_x86 - pool: - vmImage: 'vs2015-win2012r2' - steps: - - script: .\vcpkg\bootstrap-vcpkg.bat - displayName: Bootstrap vcpkg - - script: .\vcpkg\vcpkg.exe install zlib openssl boost-system boost-date-time boost-regex boost-interprocess websocketpp brotli --vcpkg-root .\vcpkg - displayName: vcpkg install dependencies - - script: mkdir build.common - displayName: Make Build Directory - - task: CMake@1 - inputs: - workingDirectory: 'build.common' - cmakeArgs: '-DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake -DCPPREST_EXCLUDE_BROTLI=OFF ..' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - - script: | - cd build.common\Release\Binaries\Debug - .\test_runner.exe *testd.dll - displayName: 'Run tests, debug' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - configuration: 'Release' - - script: | - cd build.common\Release\Binaries\Release - .\test_runner.exe *test.dll - displayName: 'Run tests, release' - - job: Windows_VS2015_x64 - pool: - vmImage: 'vs2015-win2012r2' - steps: - - script: .\vcpkg\bootstrap-vcpkg.bat - displayName: Bootstrap vcpkg - - script: .\vcpkg\vcpkg.exe install zlib openssl boost-system boost-date-time boost-regex boost-interprocess websocketpp brotli --triplet x64-windows --vcpkg-root .\vcpkg - displayName: vcpkg install dependencies - - script: mkdir build.common - displayName: Make Build Directory - - task: CMake@1 - inputs: - workingDirectory: 'build.common' - cmakeArgs: '-A x64 -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake -DCPPREST_EXCLUDE_BROTLI=OFF ..' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - platform: 'x64' - - script: | - cd build.common\Release\Binaries\Debug - .\test_runner.exe *testd.dll - displayName: 'Run tests, debug' - - task: MSBuild@1 - inputs: - solution: 'build.common/ALL_BUILD.vcxproj' - maximumCpuCount: true - platform: 'x64' - configuration: 'Release' - - script: | - cd build.common\Release\Binaries\Release - .\test_runner.exe *test.dll - displayName: 'Run tests, release' - - job: Ubuntu_1604_Apt - pool: - vmImage: 'Ubuntu 16.04' - steps: - - script: | - sudo apt-get install -y ppa-purge - sudo ppa-purge -y ppa:ondrej/php - sudo apt-get install -y libboost-atomic-dev libboost-thread-dev libboost-system-dev libboost-date-time-dev libboost-regex-dev libboost-filesystem-dev libboost-random-dev libboost-chrono-dev libboost-serialization-dev libwebsocketpp-dev openssl libssl-dev ninja-build - displayName: Apt install dependencies - - script: | - mkdir build.debug - mkdir build.release - displayName: Make Build Directories - - task: CMake@1 - inputs: - workingDirectory: 'build.debug' - cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Debug ..' - - task: CMake@1 - inputs: - workingDirectory: 'build.release' - cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Release ..' - - script: | - cd build.debug - ninja - displayName: 'Run ninja, debug' - - script: | - cd build.debug/Release/Binaries - ./test_runner *test.so - displayName: 'Run tests, debug' - - script: | - cd build.release - ninja - displayName: 'Run ninja, release' - - script: | - cd build.release/Release/Binaries - ./test_runner *test.so - displayName: 'Run tests, release' - - job: Ubuntu_1604_Vcpkg - pool: - vmImage: 'Ubuntu 16.04' - steps: - - script: | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get -y update - sudo apt-get install g++-7 ninja-build -y - ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install zlib openssl boost-system boost-date-time boost-regex websocketpp boost-thread boost-filesystem boost-random boost-chrono boost-interprocess brotli --vcpkg-root ./vcpkg - displayName: Vcpkg install dependencies - - script: | - mkdir build.debug - mkdir build.release - displayName: Make Build Directories - - task: CMake@1 - inputs: - workingDirectory: 'build.debug' - cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' - - task: CMake@1 - inputs: - workingDirectory: 'build.release' - cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' - - script: | - cd build.debug - ninja - displayName: 'Run ninja debug' - - script: | - cd build.debug/Release/Binaries - ./test_runner *test.so - displayName: 'Run Tests debug' - - script: | - cd build.release - ninja - displayName: 'Run ninja, release' - - script: | - cd build.release/Release/Binaries - ./test_runner *test.so - displayName: 'Run tests, release' + - template: azure-devops/build-ubuntu-apt.yml + parameters: + name: 'Ubuntu_1604_Apt' + image: 'Ubuntu 16.04' + - template: azure-devops/build-ubuntu-apt.yml + parameters: + name: 'Ubuntu_1804_Apt' + image: 'Ubuntu 18.04' + - template: azure-devops/build-ubuntu-vcpkg.yml + parameters: + name: 'Ubuntu_1604_Vcpkg' + image: 'Ubuntu 16.04' + - template: azure-devops/build-ubuntu-vcpkg.yml + parameters: + name: 'Ubuntu_1804_Vcpkg' + image: 'Ubuntu 18.04' - job: Android pool: vmImage: 'Ubuntu 16.04' @@ -236,10 +70,12 @@ jobs: displayName: 'Build for Android' - job: MacOS_Homebrew pool: - vmImage: 'macOS-10.13' + vmImage: 'macOS-latest' steps: - script: brew install boost openssl ninja displayName: Brew install dependencies + - script: git submodule update --init Release/libs/websocketpp + displayName: Checkout websocketpp submodule - script: | mkdir build.debug mkdir build.release @@ -279,12 +115,13 @@ jobs: displayName: 'Run ninja, release static' - job: MacOS_Vcpkg pool: - vmImage: 'macOS-10.13' + vmImage: 'macOS-latest' steps: + - script: git submodule update --init vcpkg + displayName: Checkout vcpkg submodule - script: | - brew install gcc ninja ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install zlib openssl boost-system boost-date-time boost-regex websocketpp boost-thread boost-filesystem boost-random boost-chrono boost-interprocess brotli --vcpkg-root ./vcpkg + ./vcpkg/vcpkg install zlib openssl boost-locale boost-system boost-date-time boost-regex websocketpp boost-thread boost-filesystem boost-random boost-chrono boost-interprocess brotli --vcpkg-root ./vcpkg displayName: Vcpkg install dependencies - script: | mkdir build.debug @@ -293,14 +130,14 @@ jobs: - task: CMake@1 inputs: workingDirectory: 'build.debug' - cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' + cmakeArgs: '-G Ninja -DCMAKE_MAKE_PROGRAM=$(Build.SourcesDirectory)/vcpkg/downloads/tools/ninja-1.10.1-osx/ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' - task: CMake@1 inputs: workingDirectory: 'build.release' - cmakeArgs: '-G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' + cmakeArgs: '-G Ninja -DCMAKE_MAKE_PROGRAM=$(Build.SourcesDirectory)/vcpkg/downloads/tools/ninja-1.10.1-osx/ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=../vcpkg/scripts/buildsystems/vcpkg.cmake ..' - script: | cd build.debug - ninja + $(Build.SourcesDirectory)/vcpkg/downloads/tools/ninja-1.10.1-osx/ninja displayName: 'Run ninja debug' - script: | cd build.debug/Release/Binaries @@ -308,17 +145,61 @@ jobs: displayName: 'Run Tests debug' - script: | cd build.release - ninja + $(Build.SourcesDirectory)/vcpkg/downloads/tools/ninja-1.10.1-osx/ninja displayName: 'Run ninja, release' - script: | cd build.release/Release/Binaries ./test_runner *test.dylib displayName: 'Run tests, release' - - job: MacOS_iOS + # - job: MacOS_iOS + # pool: + # vmImage: 'macOS-latest' + # steps: + # - script: | + # cd Build_iOS + # ./configure.sh + # displayName: 'Build for iOS' + - job: Ubuntu_1604_Apt_winhttppal pool: - vmImage: 'macOS-10.13' + vmImage: 'Ubuntu 16.04' steps: - script: | - cd Build_iOS - ./configure.sh - displayName: 'Build for iOS' + set -e + sudo apt -y remove php* + sudo apt install -y ppa-purge + sudo ppa-purge -y ppa:ondrej/php + unset BOOST_ROOT + sudo apt install -y libboost-atomic-dev libboost-thread-dev libboost-system-dev libboost-date-time-dev libboost-regex-dev libboost-filesystem-dev libboost-random-dev libboost-chrono-dev libboost-serialization-dev libwebsocketpp-dev openssl libssl-dev ninja-build wget + wget http://curl.haxx.se/download/curl-7.57.0.tar.gz + sudo apt install -y libtool + sudo apt install -y make + tar -xvf curl-7.57.0.tar.gz + cd curl-7.57.0 + ./buildconf + ./configure --with-ssl --prefix=/usr + make + sudo make install + cd .. + git clone https://github.com/microsoft/WinHttpPAL.git + cd WinHttpPAL + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .. + make + sudo make install + cd ../.. + mkdir build.debug + cd build.debug + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCPPREST_HTTP_CLIENT_IMPL=winhttppal .. + cd .. + mkdir build.release + cd build.release + /usr/local/bin/cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCPPREST_HTTP_CLIENT_IMPL=winhttppal .. + cd .. + ninja -C build.debug + ninja -C build.release + cd build.debug/Release/Binaries + #./test_runner *test.so + cd ../../../build.release/Release/Binaries + #./test_runner *test.so + displayName: Run build diff --git a/changelog.md b/changelog.md index fd371ec498..7a9b6dfe04 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,84 @@ +cpprestsdk (2.10.19) +* PR#1982 make Uri.is_host_loopback() only return true for localhost and 127.0.0.1 exactly. + The old behavior could potentially return "true" for URLs that were not, in fact, local, + and this could cause security issues if is_host_loopback was used in certain ways. +* PR#1711 Fix likely typo in SafeInt3.hpp, that results in error with clang 15 +* PR#1496 Support for oauth2 with "client_credentials" grant type. +* PR#1429 Add constructor from all integer types for json value. +* PR#1577 export http_exception for non Windows builds using visibility macros. + +cpprestsdk (2.10.18) +* PR#1571 Add ability to parse and emit the NT Epoch 1601-01-01T00:00:00Z +* PR#1571 Update vcpkg submodule +* Update CI configuration +-- cpprestsdk team MON, 1 Feb 2021 20:02:00 -0700 + +cpprestsdk (2.10.17) +* PR#1550 Fix year calculation for the last day of a leap year +* PR#1523 Fix wrong linking of Apple Frameworks on MacOS +* PR#1520 Define __STDC_FORMAT_MACROS when it hasn't been defined to avoid duplicate define error. +* PR#1415 Delete apparently broken .vcxprojs and .pfxes. +* Removed defunct email contact information from the readme +-- cpprestsdk team WED, 30 Dec 2020 20:08:00 -0700 + +cpprestsdk (2.10.16) +* PR#1383 CMake fixes + CMake search for OpenSSL (macOS) +* PR#1392 Update submodule websocketpp to 0.8.2 +* PR#1393 Do not report errors (such as EBADF and EINVAL) from setsockopt here, since this is a performance optimization only, and hard errors will be picked up by the following operation +* PR#1379 Fix compilation with GCC 4.8/4.9, which was broken by commit 53fab3a. +* PR#1328 Add support for HTTP redirection in ASIO and WinHTTP-based http_clients +* PR#1332 Fix more http test build fails in certain configurations +* PR#1370 Remove redundant std::move noted by gcc 9.2 (-Wredundant-move) +* PR#1372 Static analyzer (PVS Studio) fixes +* PR#1350 Expose json::value::parse for UTF8 string on Windows +* PR#1344 libcpprestsdk: fix building as a static library +-- cpprestsdk team FRI, 24 Apr 2020 16:56:00 -0700 + +cpprestsdk (2.10.15) +* Extremely special thanks to @garethsb-sony for a large number of contributions in this release +* PR#1209 Workarounds for two GCC 4.7.2 bugs with lambda functions +* PR#1220 Fix SxS debug-release builds with Visual Studio +* PR#1219 Fix "Data" to "Date" in the HTTP Server API mapping, and clarify that the indices of these values match the HTTP_HEADER_ID values for HTTP_REQUEST_HEADERS but *not* HTTP_RESPONSE_HEADERS +* PR#1196 Fixing of connections_and_errors::cancel_with_error test which sometimes fires false positive error "There are no pending calls to next_request." +* PR#1233 Trim whitespace and nulls the same way. +* PR#1248 Avoid using permissive- with ZW which breaks VS2019 +* PR#1182 Support for WinHTTPAL curl-to-WinHTTP adapter +* PR#1253 http_server_httpsys.cpp requires linking against httpapi.lib, http_client_winhttp.cpp does not. +* PR#1263 Remove trailing slash on websocketpp submodule url, which causes checkout failure on CircleCI with git 2.22.0 +* PR#1293 Update vcpkg and remove tests that look for web servers that no longer exist +* PR#1288 Fix test case broken by commit f4c863b +* PR#1276 Added comparison overrides to utility::datetime +* PR#1289 Fix various warnings reported by gcc 9.3, and possibly earlier versions +* PR#1334 Update vcpkg and boost on Android +* PR#1306 Change default installation directory for cmake files to cmake/cpprestsdk +* PR#1330 Use LC_ALL_MASK rather than LC_ALL when calling newlocale +* PR#1310 Add TCP_NODELAY to disable Nagle's algorithm in Boost.ASIO-based http_client +* PR#1335 Turn VS2015 back on now that vcpkg is fixed. +* PR#1322 Enable HTTP compression support on all platforms +* PR#1340 Add Ubuntu 18.04 testing. +* PR#1342 Use C++11 synchronization classes under macOS too +* PR#1339 Fix tcp::resolver data race in the asio backend and be defensive against empty results +-- cpprestsdk team THR, 22 Feb 2020 08:31:00 -0800 + +cpprestsdk (2.10.14) +* Potential breaking change warning: This release changes the "default" proxy for the WinHTTP backend to go back to WINHTTP_ACCESS_TYPE_DEFAULT_PROXY. See https://github.com/microsoft/cpprestsdk/commit/60e067e71aebebdda5d82955060f5f0821c9df1d for more details. To get automatic WPAD behavior, set the proxy to auto detect. +* macOS with Brew and iOS builds have been disabled and are no longer being tested because our dependency boost for ios project appears to be broken with current releases of XCode as on the Azure Pipelines machines. We are interested in macOS / iOS folks who know what's going on here in contributing a repair to turn this back on. +* PR#1133 Add switches to make apiscan happy. +* PR#1130 json: {"meow"} is not a valid object +* PR#1150 Undefine compress if it is defined by zconf.h +* PR#1156 Fix broken CI Builds +* PR#1155 Use EVP_MAX_MD_SIZE instead of HMAC_MAX_MD_CBLOCK +* PR#1145 Remove the address_configured flag on tcp::resolver::query +* PR#1143 add ping and pong to message handler +* PR#539 Fix reusing ASIO http_client connecting to HTTPS server via proxy +* PR#1175 Fix issue #1171: Order of object destruction +* PR#1183 FIX: SSL proxy tunnel support with basic auth +* PR#1184 Fix profile being set on the compiler instead of the linker. +* PR#1185 Update boost-for-android for Android NDK r20 and disable macOS Homebrew. +* PR#1187 Replace CPPREST_TARGET_XP with version checks, remove ""s, and other cleanup +* PR#1188 Remove proxy settings detection behavior in "default proxy mode." +-- cpprestsdk team TUE, 16 Jul 2019 09:06:00 +0200 + cpprestsdk (2.10.13) * PR#1120 Fix off by one error in leap years before year 2000, and bad day names * PR#1117 Parse and emit years from 1900 to 9999, and remove environment variable dependence on Android diff --git a/vcpkg b/vcpkg index c7d4696a88..b759049a36 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit c7d4696a8857235ec8e3497e43f0d6fe3d4b5396 +Subproject commit b759049a36728d18260963799a56e6b19cb4a2ef